Automapper扩展方法
问题描述
系统中实现了一个自定义的PagedList
/// <summary> /// Paged list interface /// </summary> public interface IPagedList { int PageIndex { get; } int PageSize { get; } int TotalCount { get; } int TotalPages { get; } bool HasPreviousPage { get; } bool HasNextPage { get; } } /// <summary> /// Paged list interface /// </summary> public interface IPagedList<T> : IList<T>, IPagedList { } /// <summary> /// Paged list /// </summary> /// <typeparam name="T">T</typeparam> [Serializable] public class PagedList<T> : List<T>, IPagedList<T> { /// <summary> /// Ctor /// </summary> /// <param name="source">source</param> /// <param name="pageIndex">Page index</param> /// <param name="pageSize">Page size</param> public PagedList(IQueryable<T> source, int pageIndex, int pageSize) { int total = source.Count(); this.TotalCount = total; this.TotalPages = total / pageSize; if (total % pageSize > 0) TotalPages++; this.PageSize = pageSize; this.PageIndex = pageIndex; this.AddRange(source.Skip(pageIndex * pageSize).Take(pageSize).ToList()); } /// <summary> /// Ctor /// </summary> /// <param name="source">source</param> /// <param name="pageIndex">Page index</param> /// <param name="pageSize">Page size</param> public PagedList(IList<T> source, int pageIndex, int pageSize) { TotalCount = source.Count(); TotalPages = TotalCount / pageSize; if (TotalCount % pageSize > 0) TotalPages++; this.PageSize = pageSize; this.PageIndex = pageIndex; this.AddRange(source.Skip(pageIndex * pageSize).Take(pageSize).ToList()); } /// <summary> /// Ctor /// </summary> /// <param name="source">source</param> /// <param name="pageIndex">Page index</param> /// <param name="pageSize">Page size</param> /// <param name="totalCount">Total count</param> public PagedList(IEnumerable<T> source, int pageIndex, int pageSize, int totalCount) { TotalCount = totalCount; TotalPages = TotalCount / pageSize; if (TotalCount % pageSize > 0) TotalPages++; this.PageSize = pageSize; this.PageIndex = pageIndex; this.AddRange(source); } public int PageIndex { get; private set; } public int PageSize { get; private set; } public int TotalCount { get; private set; } public int TotalPages { get; private set; } public bool HasPreviousPage { get { return (PageIndex > 0); } } public bool HasNextPage { get { return (PageIndex + 1 < TotalPages); } } }
转换失败,AutoMapper可不认识PagedList
[TestMethod] public void MapTest2() { Mapper.CreateMap<User, UserViewModel>(); var userList = new List<User>() { new User() {Name = "Name1", UserId = "UserId1"}, new User() {Name = "Name2", UserId = "UserId2"}, new User() {Name = "Name3", UserId = "UserId3"}, new User() {Name = "Name4", UserId = "UserId4"}, new User() {Name = "Name5", UserId = "UserId5"}, new User() {Name = "Name6", UserId = "UserId6"}, new User() {Name = "Name7", UserId = "UserId7"}, }; var pagedList = new PagedList<User>(userList, 0, 5); Mapper.Map<PagedList<User>, PagedList<UserViewModel>>(pagedList);//Exception }
解决方案
/// <summary> /// The collection extensions. /// </summary> public static class ObjectExtensions { private static readonly MethodInfo mapMethod; private static readonly ConcurrentDictionary<Tuple<Type, Type>, Tuple<MethodInfo, Type>> methodsMapper = new ConcurrentDictionary<Tuple<Type, Type>, Tuple<MethodInfo, Type>>(); static ObjectExtensions() { mapMethod = (typeof(Mapper)).GetMethods().FirstOrDefault(_ => _.Name == "Map" && _.GetParameters().Length == 1); } public static T MapTo<T>(this IPagedList tList) where T : class { var totalCount = tList.TotalCount; var pageIndex = tList.PageIndex; var pageSize = tList.PageSize; var t = methodsMapper.GetOrAdd(new Tuple<Type, Type>(tList.GetType(), typeof(T)), _ => { var targetGenericArguments = typeof(T).GenericTypeArguments[0]; var targetGenericArgumentsIEnumerableType = typeof(IEnumerable<>).MakeGenericType(targetGenericArguments); return new Tuple<MethodInfo, Type>(mapMethod.MakeGenericMethod(targetGenericArgumentsIEnumerableType), typeof(PagedList<>).MakeGenericType(targetGenericArguments)); }); var rtn2 = t.Item1.Invoke(null, new object[] { tList }); var o2 = Activator.CreateInstance(t.Item2, rtn2, pageIndex, pageSize, totalCount) as T; return o2; } public static T MapTo<T>(this object o) where T : class { //way1 //var mapMethod = (typeof(Mapper)).GetMethods().FirstOrDefault(_ => _.Name == "Map" && _.GetParameters().Length == 1 && _.GetGenericArguments().Length == 2 ); //var m2 = mapMethod.MakeGenericMethod(o.GetType(), typeof (T)); //return m2.Invoke(null, new[] { o }) as T; //way2 return Mapper.Map<T>(o); } public static void MapTo<S,T>(this S o,T t) where T : class { Mapper.Map(o,t); } }
测试通过
[TestMethod] public void MapTest2() { Mapper.CreateMap<User, UserViewModel>(); var userList = new List<User>() { new User() {Name = "Name1", UserId = "UserId1"}, new User() {Name = "Name2", UserId = "UserId2"}, new User() {Name = "Name3", UserId = "UserId3"}, new User() {Name = "Name4", UserId = "UserId4"}, new User() {Name = "Name5", UserId = "UserId5"}, new User() {Name = "Name6", UserId = "UserId6"}, new User() {Name = "Name7", UserId = "UserId7"}, }; var pagedList = new PagedList<User>(userList, 0, 5); var vmPagedList = pagedList.MapTo<PagedList<UserViewModel>>(); Assert.IsTrue(vmPagedList.TotalPages == 2 && vmPagedList.PageSize == 5 && vmPagedList.PageIndex == 0 ); }
总结
运行时动态获取泛型参数并执行Mapper.Map<IEnumerable<TSource>, IEnumerable<TDestination>>,并且使用ConcurrentDictionary缓存MethodInfo提高性能。