初识AutoMapper

在开始本篇文章之前,先来思考一个问题:一个项目分多层架构,如显示层、业务逻辑层、服务层、数据访问层。层与层访问需要数据载体,也就是类。如果多层通用一个类,一则会暴露出每层的字段,二者会使类字段很多,而且会出现很多冗余字段,这种方式是不可取的;如果每层都使用不同的类,则层与层调用时,一个字段一个字段的赋值又会很麻烦。针对第二种情况,可以使用AutoMapper来帮助我们实现类字段的赋值及转换。

AutoMapper是一个对象映射器,它可以将一个一种类型的对象转换为另一种类型的对象。AutoMapper提供了映射规则及操作方法,使我们不用过多配置就可以映射两个类。

安装AutoMapper

通过Nuget安装AutoMapper,本次使用版本为6.2.2。

AutoMapper配置

初始化

先创建两个类用于映射:

public class ProductEntity
{
public string Name { get; set; }
public decimal Amount { get; set; }
} public class ProductDTO
{
public string Name { get; set; }
public decimal Amount { get; set; }
}

Automapper可以使用静态类和实例方法来创建映射,下面分别使用这两种方式来实现 ProductEntity -> ProductDTO的映射。

  • 使用静态方式
Mapper.Initialize(cfg => cfg.CreateMap<ProductEntity, ProductDTO>());
var productDTO = Mapper.Map<ProductDTO>(productEntity);
  • 使用实例方法
MapperConfiguration configuration = new MapperConfiguration(cfg => cfg.CreateMap<ProductEntity, ProductDTO>());
var mapper = configuration.CreateMapper();
var productDTO = mapper.Map<ProductDTO>(productEntity);

完整的例子:

[TestMethod]
public void TestInitialization()
{
var productEntity = new ProductEntity()
{
Name = "Product" + DateTime.Now.Ticks,
Amount = 10
}; Mapper.Initialize(cfg => cfg.CreateMap<ProductEntity, ProductDTO>());
var productDTO = Mapper.Map<ProductDTO>(productEntity); Assert.IsNotNull(productDTO);
Assert.IsNotNull(productDTO.Name);
Assert.IsTrue(productDTO.Amount > 0);
}

Profiles设置

除了使用以上两总方式类配置映射关系,也可以使用Profie配置来实现映射关系。

创建自定义的Profile需要继承Profile类:

public class MyProfile : Profile
{
public MyProfile()
{
CreateMap<ProductEntity, ProductDTO>();
// Other mapping configurations
}
} 

完成例子:

[TestMethod]
public void TestProfile()
{
var productEntity = new ProductEntity()
{
Name = "Product" + DateTime.Now.Ticks,
Amount = 10
}; var configuration = new MapperConfiguration(cfg => cfg.AddProfile<MyProfile>());
var productDTO = configuration.CreateMapper().Map<ProductDTO>(productEntity); Assert.IsNotNull(productDTO);
Assert.IsNotNull(productDTO.Name);
Assert.IsTrue(productDTO.Amount > 0);
}

除了使用AddProfile,也可以使用AddProfiles添加多个配置;同样,可以同时使用Mapper和Profile,也可以添加多个配置:

var configuration = new MapperConfiguration(cfg =>
{
cfg.AddProfile<MyProfile>();
cfg.CreateMap<ProductEntity, ProductDTO>();
});

扁平化映射

AutoMapper先映射名字一致的字段,如果没有,则会尝试使用以下规则来映射:

  • 目标中字段去掉前缀“Get”后的部分
  • 分割目标字段(根据Pascal命名方式)为单个单词

先创建用到的映射类:

public class Product
{
public Supplier Supplier { get; set; }
public string Name { get; set; } public decimal GetAmount()
{
return 10;
}
} public class Supplier
{
public string Name { get; set; }
} public class ProductDTO
{
public string SupplierName { get; set; }
public decimal Amount { get; set; }
}

AutoMapper会自动实现Product.Supplier.Name -> ProductDTO.SupplierName, Product.GetTotal -> ProductDTO.Total的映射。

[TestMethod]
public void TestFalttening()
{
var supplier = new Supplier()
{
Name = "Supplier" + DateTime.Now.Ticks
}; var product = new Product()
{
Supplier = supplier,
Name = "Product" + DateTime.Now.Ticks
}; Mapper.Initialize(cfg => cfg.CreateMap<Product, ProductDTO>()); var productDTO = Mapper.Map<ProductDTO>(product); Assert.IsNotNull(productDTO);
Assert.IsNotNull(productDTO.SupplierName);
Assert.IsTrue(productDTO.Amount > 0);
}

集合验证

AutoMapper除了可以映射单个对象外,也可以映射集合对象。AutoMapper源集合类型支持以下几种:

  • IEnumerable
  • IEnumerable<T>
  • ICollection
  • ICollection<T>
  • IList
  • IList<T>
  • List<T>
  • Arrays

简单类型映射:

public class Source
{
public int Value { get; set; }
} public class Destination
{
public int Value { get; set; }
} [TestMethod]
public void TestCollectionSimple()
{
Mapper.Initialize(cfg => cfg.CreateMap<Source, Destination>()); var sources = new[]
{
new Source {Value = 1},
new Source {Value = 2},
new Source {Value = 3}
}; IEnumerable<Destination> ienumerableDest = Mapper.Map<Source[], IEnumerable<Destination>>(sources);
ICollection<Destination> icollectionDest = Mapper.Map<Source[], ICollection<Destination>>(sources);
IList<Destination> ilistDest = Mapper.Map<Source[], IList<Destination>>(sources);
List<Destination> listDest = Mapper.Map<Source[], List<Destination>>(sources);
Destination[] arrayDest = Mapper.Map<Source[], Destination[]>(sources);
}  

复杂对象映射:

public class Order
{
private IList<OrderLine> _lineItems = new List<OrderLine>(); public OrderLine[] LineItems { get { return _lineItems.ToArray(); } } public void AddLineItem(OrderLine orderLine)
{
_lineItems.Add(orderLine);
}
} public class OrderLine
{
public int Quantity { get; set; }
} public class OrderDTO
{
public OrderLineDTO[] LineItems { get; set; }
} public class OrderLineDTO
{
public int Quantity { get; set; }
} [TestMethod]
public void TestCollectionNested()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Order, OrderDTO>();
cfg.CreateMap<OrderLine, OrderLineDTO>();
}); var order = new Order();
order.AddLineItem(new OrderLine {Quantity = 10});
order.AddLineItem(new OrderLine {Quantity = 20});
order.AddLineItem(new OrderLine {Quantity = 30}); var orderDTO = Mapper.Map<OrderDTO>(order);
Assert.IsNotNull(orderDTO);
Assert.IsNotNull(orderDTO.LineItems);
Assert.IsTrue(orderDTO.LineItems.Length > 0);
}

投影及条件映射

投影(指定字段)

除了以上使用的自动映射规则,AutoMapper还可以指定映射方式。下面使用ForMemeber指定字段的映射,将一个时间值拆分映射到日期、时、分:

public class Calendar
{
public DateTime CalendarDate { get; set; }
public string Title { get; set; }
} public class CalendarModel
{
public DateTime Date { get; set; }
public int Hour { get; set; }
public int Minute { get; set; }
public string Title { get; set; }
} [TestMethod]
public void TestProjection()
{
var calendar = new Calendar()
{
Title = "2018年日历",
CalendarDate = new DateTime(2018, 1, 1, 11, 59, 59)
}; Mapper.Initialize(cfg => cfg
.CreateMap<Calendar, CalendarModel>()
.ForMember(dest => dest.Date, opt => opt.MapFrom(src =>src.CalendarDate.Date))
.ForMember(dest => dest.Hour, opt => opt.MapFrom(src => src.CalendarDate.Hour))
.ForMember(dest => dest.Minute, opt => opt.MapFrom(src => src.CalendarDate.Minute))); var calendarModel = Mapper.Map<CalendarModel>(calendar); Assert.AreEqual(calendarModel.Date.Ticks, new DateTime(2018, 1, 1).Ticks);
Assert.AreEqual(calendarModel.Hour, 11);
Assert.AreEqual(calendarModel.Minute, 59);
}

条件映射

有些情况下,我们会考虑添加映射条件,比如,某个值不符合条件时,不允许映射。针对这种情况可以使用ForMember中的Condition:

public class Source
{
public int Value { get; set; }
} public class Destination
{
public uint Value { get; set; }
} [TestMethod]
public void TestConditionByCondition()
{
var source = new Source()
{
Value = 3
}; //如果Source.Value > 0, 则执行映射;否则,映射失败
Mapper.Initialize(cfg => cfg
.CreateMap<Source, Destination>()
.ForMember(dest => dest.Value, opt => opt.Condition(src => src.Value > 0))); var destation = Mapper.Map<Destination>(source); //如果不符合条件,则抛出异常 Assert.IsTrue(destation.Value.Equals(3));
}

如果要映射的类符合一定的规则,而且有很多,针对每个类都创建一个CreaterMapper会很麻烦。可以使用AddConditionalObjectMapper指定对象映射规则,这样就不用每个映射关系都添加一个CreateMapper。另外,也可以使用AddMemberConfiguration指定字段的映射规则,比如字段的前后缀:

public class Product
{
public string Name { get; set; }
public int Count { get; set; }
} public class ProductModel
{
public string NameModel { get; set; }
public int CountMod { get; set; }
} [TestMethod]
public void TestConditionByConfiguration()
{
var product = new Product()
{
Name = "Product" + DateTime.Now.Ticks,
Count = 10
}; var config = new MapperConfiguration(cfg =>
{
//对象映射规则: 通过以下配置,可以映射所有”目标对象的名称“等于“源对象名称+Model”的类,而不用单个添加CreateMapper映射
cfg.AddConditionalObjectMapper().Where((s, d) => d.Name == s.Name + "Model"); //字段映射规则: 通过以下配置,可以映射“源字段”与“目标字段+Model或Mod”的字段
cfg.AddMemberConfiguration().AddName<PrePostfixName>(_ => _.AddStrings(p => p.DestinationPostfixes, "Model", "Mod"));
}); var mapper = config.CreateMapper(); var productModel = mapper.Map<ProductModel>(product); Assert.IsTrue(productModel.CountMod == 10);
}

需要注意的一点是,添加了以上配置,如果目标对象中有字段没有映射到,则会抛出异常。

值转换

如果配置了值转换,AutoMapper会将修改转换后的值以符合配置的规则。比如,配置目标对象中的值添加符号“@@”:

public class Source
{
public string Name { get; set; }
} public class Destination
{
public string Name { get; set; }
} [TestMethod]
public void TestValueTransfer()
{
var source = new Source()
{
Name = "Bob"
}; Mapper.Initialize(cfg =>
{
cfg.CreateMap<Source, Destination>();
cfg.ValueTransformers.Add<string>(val => string.Format("@{0}@", val));
}); var destation = Mapper.Map<Destination>(source); Assert.AreEqual("@Bob@", destation.Name);
} 

空值替换

如果要映射的值为Null,则可以使用NullSubstitute指定Null值的替换值:

public class Source
{
public string Name { get; set; }
} public class Destination
{
public string Name { get; set; }
} [TestMethod]
public void TestValueTransfer()
{
var source = new Source()
{
}; Mapper.Initialize(cfg =>
{
cfg.CreateMap<Source, Destination>()
.ForMember(dest => dest.Name, opt => opt.NullSubstitute("其他值"));
}); var destation = Mapper.Map<Destination>(source); Assert.AreEqual("其他值", destation.Name);
}

配置验证及设置

配置了映射,但是如何确定是否映射成功或者是否有字段没有映射呢?可以添加Mapper.AssertConfigurationIsValid();来验证是否映射成功。默认情况下,目标对象中的字段都被映射到后,AssertConfigurationIsValid才会返回True。也就是说,源对象必须包含所有目标对象,这样在大多数情况下不是我们想要的,我们可以使用下面的方法来指定验证规则:

  • 指定单个字段不验证
  • 指定整个Map验证规则
public class Product
{
public string Name { get; set; }
public int Amount { get; set; }
} public class ProductModel
{
public string Name { get; set; }
public int Amount { get; set; }
public string ViewName { get; set; }
} public class ProductDTO
{
public string Name { get; set; }
public int Amount { get; set; }
public string ViewName { get; set; }
} [TestMethod]
public void TestValidation()
{
var product = new Product()
{
Name = "Product" + DateTime.Now.Ticks,
Amount = 10
}; Mapper.Initialize(cfg =>
{
//1. 指定字段映射方式
cfg.CreateMap<Product, ProductModel>()
.ForMember(dest => dest.ViewName, opt => opt.Ignore()); //如果不添加此设置,会抛出异常 //2. 指定整个对象映射方式
//MemberList:
// Source: 检查源对象所有字段映射成功
// Destination:检查目标对象所有字段映射成功
// None: 跳过验证
cfg.CreateMap<Product, ProductDTO>(MemberList.Source);
}); var productModel = Mapper.Map<ProductModel>(product);
var productDTO = Mapper.Map<ProductDTO>(product); //验证映射是否成功
Mapper.AssertConfigurationIsValid();
}

设置转换前后行为

有的时候你可能会在创建映射前后对数据做一些处理,AutoMapper就提供了这种方式:

public class Source
{
public string Name { get; set; }
public int Value { get; set; }
} public class Destination
{
public string Name { get; set; }
public int Value { get; set; }
} [TestMethod]
public void TestBeforeOrAfter()
{
var source = new Source()
{
Name = "Product" + DateTime.Now.Ticks,
}; Mapper.Initialize(cfg =>
{
cfg.CreateMap<Source, Destination>()
.BeforeMap((src, dest) => src.Value = src.Value + 10)
.AfterMap((src, dest) => dest.Name = "Pobin");
}); var productModel = Mapper.Map<Destination>(source); Assert.AreEqual("Pobin", productModel.Name);
}

反向映射

从6.1.0开始,AutoMapper通过调用Reverse可以实现反向映射。反向映射根据初始化时创建的正向映射规则来做反向映射:

public class Order
{
public decimal Total { get; set; }
public Customer Customer { get; set; }
} public class Customer
{
public string Name { get; set; }
} public class OrderDTO
{
public decimal Total { get; set; }
public string CustomerName { get; set; }
} [TestMethod]
public void TestReverseMapping()
{
var customer = new Customer
{
Name = "Tom"
}; var order = new Order
{
Customer = customer,
Total = 20
}; Mapper.Initialize(cfg => {
cfg.CreateMap<Order, OrderDTO>()
.ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Customer.Name)) //正向映射规则
.ReverseMap(); //设置反向映射
}); //正向映射
var orderDTO = Mapper.Map<OrderDTO>(order); //反向映射:使用ReverseMap,不用再创建OrderDTO -> Order的映射,而且还能保留正向的映射规则
var orderConverted = Mapper.Map<Order>(orderDTO); Assert.IsNotNull(orderConverted.Customer);
Assert.AreEqual("Tom", orderConverted.Customer.Name);
}

如果反向映射中不想使用原先的映射规则,也可以取消掉:

Mapper.Initialize(cfg => {
cfg.CreateMap<Order, OrderDTO>()
.ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Customer.Name)) //正向映射规则
.ReverseMap()
.ForPath(src => src.Customer.Name, opt => opt.Ignore()); //设置反向映射
});

自定义转换器

有些情况下目标字段类型和源字段类型不一致,可以通过类型转换器实现映射,类型转换器有三种实现方式:

void ConvertUsing(Func<TSource, TDestination> mappingFunction);
void ConvertUsing(ITypeConverter<TSource, TDestination> converter);
void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>;

下面通过一个例子来演示下以上三种类型转换器的使用方式:

namespace AutoMapperSummary
{
[TestClass]
public class CustomerTypeConvert
{
public class Source
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
} public class Destination
{
public int Value1 { get; set; }
public DateTime Value2 { get; set; }
public Type Value3 { get; set; }
} public class DateTimeTypeConverter : ITypeConverter<string, DateTime>
{
public DateTime Convert(string source, DateTime destination, ResolutionContext context)
{
return System.Convert.ToDateTime(source);
}
} public class TypeTypeConverter : ITypeConverter<string, Type>
{
public Type Convert(string source, Type destination, ResolutionContext context)
{
return Assembly.GetExecutingAssembly().GetType(source);
}
} [TestMethod]
public void TestTypeConvert()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<string, int>().ConvertUsing((string s) => Convert.ToInt32(s));
cfg.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter());
cfg.CreateMap<string, Type>().ConvertUsing<TypeTypeConverter>();
cfg.CreateMap<Source, Destination>();
}); config.AssertConfigurationIsValid(); //验证映射是否成功 var source = new Source
{
Value1 = "20",
Value2 = "2018/1/1",
Value3 = "AutoMapperSummary.CustomerTypeConvert+Destination"
}; var mapper = config.CreateMapper();
var destination = mapper.Map<Source, Destination>(source); Assert.AreEqual(typeof(Destination), destination.Value3);
}
}
}

自定义解析器

使用AutoMapper的自带解析规则,我们可以很方便的实现对象的映射。比如:源/目标字段名称一致,“Get/get + 源字段“与"目标字段"一致等。除了这些简单的映射,还可以使用ForMember指定字段映射。但是,某些情况下,解析规则会很复杂,使用自带的解析规则无法实现。这时可以自定义解析规则,可以通过以下三种方式使用自定义的解析器:

ResolveUsing<TValueResolver>
ResolveUsing(typeof(CustomValueResolver))
ResolveUsing(aValueResolverInstance)

下面通过一个例子来演示如何使用自定义解析器:

public class Source
{
public string FirstName { get; set; }
public string LastName { get; set; }
} public class Destination
{
public string Name { get; set; }
} /// <summary>
/// 自定义解析器: 组合姓名
/// </summary>
public class CustomResolver : IValueResolver<Source, Destination, string>
{
public string Resolve(Source source, Destination destination, string destMember, ResolutionContext context)
{
if (source != null && !string.IsNullOrEmpty(source.FirstName) && !string.IsNullOrEmpty(source.LastName))
{
return string.Format("{0} {1}", source.FirstName, source.LastName);
} return string.Empty;
}
} [TestMethod]
public void TestResolver()
{
Mapper.Initialize(cfg =>
cfg.CreateMap<Source, Destination>()
.ForMember(dest => dest.Name, opt => opt.ResolveUsing<CustomResolver>())); Mapper.AssertConfigurationIsValid(); var source = new Source
{
FirstName = "Michael",
LastName = "Jackson"
}; var destination = Mapper.Map<Source, Destination>(source);
Assert.AreEqual("Michael Jackson", destination.Name);
}

AutoMapper封装

AutoMapper功能很强大,自定义配置支持也非常好,但是真正项目中使用时却很少用到这么多功能,而且一般都会对AutoMapper进一步封装使用。一方面使用起来方面,另外一方面也可以使代码统一。下面的只是做一个简单的封装,还需要结合实际项目使用:

/// <summary>
/// AutoMapper帮助类
/// </summary>
public class AutoMapperManager
{
private static readonly MapperConfigurationExpression MapperConfiguration = new MapperConfigurationExpression(); static AutoMapperManager()
{
} private AutoMapperManager()
{
AutoMapper.Mapper.Initialize(MapperConfiguration);
} public static AutoMapperManager Instance { get; } = new AutoMapperManager(); /// <summary>
/// 添加映射关系
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TDestination"></typeparam>
public void AddMap<TSource, TDestination>() where TSource : class, new() where TDestination : class, new()
{
MapperConfiguration.CreateMap<TSource, TDestination>();
} /// <summary>
/// 获取映射值
/// </summary>
/// <typeparam name="TDestination"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public TDestination Map<TDestination>(object source) where TDestination : class, new()
{
if (source == null)
{
return default(TDestination);
} return Mapper.Map<TDestination>(source);
} /// <summary>
/// 获取集合映射值
/// </summary>
/// <typeparam name="TDestination"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public IEnumerable<TDestination> Map<TDestination>(IEnumerable source) where TDestination : class, new()
{
if (source == null)
{
return default(IEnumerable<TDestination>);
} return Mapper.Map<IEnumerable<TDestination>>(source);
} /// <summary>
/// 获取映射值
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TDestination"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public TDestination Map<TSource, TDestination>(TSource source) where TSource : class, new () where TDestination : class, new()
{
if (source == null)
{
return default(TDestination);
} return Mapper.Map<TSource, TDestination>(source);
} /// <summary>
/// 获取集合映射值
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TDestination"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public IEnumerable<TDestination> Map<TSource, TDestination>(IEnumerable<TSource> source) where TSource : class, new() where TDestination : class, new()
{
if (source == null)
{
return default(IEnumerable<TDestination>);
} return Mapper.Map<IEnumerable<TSource>, IEnumerable<TDestination>>(source);
} /// <summary>
/// 读取DataReader内容
/// </summary>
/// <typeparam name="TDestination"></typeparam>
/// <param name="reader"></param>
/// <returns></returns>
public IEnumerable<TDestination> Map<TDestination>(IDataReader reader)
{
if (reader == null)
{
return new List<TDestination>();
} var result = Mapper.Map<IEnumerable<TDestination>>(reader); if (!reader.IsClosed)
{
reader.Close();
} return result;
}
}

总结

本篇文章列举了AutoMapper的基本使用方式,更多的使用可以参考官方文档:http://automapper.readthedocs.io/en/latest/index.html

AutoMapper 使用总结的更多相关文章

  1. 恋爱虽易,相处不易:当EntityFramework爱上AutoMapper

    剧情开始 为何相爱? 相处的问题? 女人的伟大? 剧情收尾? 有时候相识即是一种缘分,相爱也不需要太多的理由,一个眼神足矣,当EntityFramework遇上AutoMapper,就是如此,恋爱虽易 ...

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

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

  3. AutoMapper

    什么是AutoMapper? AutoMapper是一个对象和对象间的映射器.对象与对象的映射是通过转变一种类型的输入对象为一种不同类型的输出对象工作的.让AutoMapper有意思的地方在于它提供了 ...

  4. AutoMapper随笔记

    平台之大势何人能挡? 带着你的Net飞奔吧! http://www.cnblogs.com/dunitian/p/4822808.html#skill 先看效果:(完整Demo:https://git ...

  5. AutoMapper:Unmapped members were found. Review the types and members below. Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type

    异常处理汇总-后端系列 http://www.cnblogs.com/dunitian/p/4523006.html 应用场景:ViewModel==>Mode映射的时候出错 AutoMappe ...

  6. AutoMapper的介绍与使用(一)

    软件环境 vs2015 asp.net mvc 5 .NET Framework 4.5.2 AutoMapper 5.2.0.0 AutoMapper安装 新建asp.net mvc 项目 Auto ...

  7. AutoMapper使用中的问题

    指定值只会执行一次 public class MomanBaseProfile : Profile { public MomanBaseProfile() { CreateMap<Request ...

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

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

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

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

  10. 【道德经】漫谈实体、对象、DTO及AutoMapper的使用

    写在前面 实体和值对象 实体和对象 故常无欲以观其妙,常有欲以观其徼 初始实体和演化实体 代码中的DTO AutoMapper实体转换 后记 实体(Entity).对象(Object).DTO(Dat ...

随机推荐

  1. 运行期以索引获取tuple元素-C++14

    在编译期很容易根据索引来获取对应位置的元素,因为 tuple 的帮助函数 std::get<N>(tp) 就能获取 tuple 中第 N 个元素.然而我们却不能直接在运行期通过变量来获取 ...

  2. object-fit?

    知道有这个属性存在,是一个很偶然的机会.有一天,设计部的一个小伙伴给了我一个网址,说很有个性,让我看一下,当发现一个很有意思的效果时,作为一个前端小兵的我当然是第一时间开始审查元素,然后看到了这个从没 ...

  3. Java开发小技巧(三):Maven多工程依赖项目

    前言 本篇文章基于Java开发小技巧(二):自定义Maven依赖中创建的父工程project-monitor实现,运用我们自定义的依赖包进行多工程依赖项目的开发. 下面以多可执行Jar包项目的开发为例 ...

  4. opensslBIO系列之2---BIO结构和BIO相关文件介绍

    BIO结构和BIO相关文件介绍     (作者:DragonKing Mail:wzhah@263.net 公布于:http://gdwzh.126.com openssl专业论坛)          ...

  5. Implement Queue using Stacks(用栈实现队列)

    Implement the following operations of a queue using stacks. push(x) -- Push element x to the back of ...

  6. Oracle之PLSQL

    PL/SQL Developer是一个集成开发环境,专门开发面向Oracle数据库的应用.PL/SQL也是一种程序语言,叫做过程化SQL语言(Procedural Language/SQL)它是Ora ...

  7. ansible编译httpd playbook示例

    以下是playbook的内容.它的处理流程是: 1.先在本地下载apr,apr-util,httpd共3个.tar.gz文件. 2.解压这3个文件. 3.安装pcre和pcre-devel依赖包. 4 ...

  8. mango(mango ORM框架介绍)

    官网地址:http://www.jfaster.org/ mango的中文名是"芒果",它是一个极速分布式ORM框架.目前已有十多个大型线上项目在使用mango,在某一支付系统中, ...

  9. .Net Core在X86上实现Interlocked.Increment(ref long)的方式

    因为在X86上long会被分割为两个int进行操作, 那么Interlocked.Increment的实现成为了一个问题. 在一番搜索后未发现有现成的文章解释这个问题,于是我就动手分析了. 这篇是笔记 ...

  10. Sphinx学习笔记2

    因为网站搜索的需要,启动了一个搜索引擎项目,其实也算不上完整的搜索引擎,需求很简单,如下:     1)搜索产品名.类别名.品牌名.副标题.关键字等字段     2)数据量目前为13000左右,未来可 ...