Automapper解析器性能

时间:2014-10-20 22:48:43

标签: c# automapper

当我正在处理涉及多个DTO和它们之间的映射的大型项目时,我有点担心在两个类和第三个源之间解析和映射值的各种方法的性能。所以我编写了这个小程序来测试从提供的变量中映射一些常见属性的各种方法。

#region

using System;
using System.Linq;
using System.Threading;
using AutoMapper;

#endregion

namespace ConsoleApplication1
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Mapper.Reset();
            Thread.Sleep(2000);
            Console.WriteLine("Starting");
            for (int i = 0; i < 10; i++)
            {
                Test1();
                Test2();
                Test3();
                Test4();
            }
            Console.WriteLine("Finished");
            Thread.Sleep(2000);
        }

        private static void Test1()
        {
            Mapper.Reset();
            Mapper.CreateMap<Quote, Policy>()
                .ForMember(m => m.Id, o => o.Ignore())
                .ForMember(m => m.Number, o => o.Ignore())
                .ForMember(m => m.AuditName, o => o.ResolveUsing<UserNameResolver>())
                .ForMember(m => m.AuditTime, o => o.ResolveUsing<AuditTimeResolver>());
            Mapper.AssertConfigurationIsValid();
            Mapper.Configuration.Seal();

            for (int i = 0; i < 10000; i++)
            {
                UserSetting us = new UserSetting {UserName = "Test", ExecTime = DateTime.Now};
                Quote q = new Quote {Id = i, Description = i.ToString("0000000")};
                Policy p = Mapper.Map<Policy>(q, o => o.Items[Constants.UserSetting] = us);
            }
        }

        private static void Test2()
        {
            Mapper.Reset();
            Mapper.CreateMap<Quote, Policy>()
                .ForMember(m => m.Id, o => o.Ignore())
                .ForMember(m => m.Number, o => o.Ignore())
                .ForMember(m => m.AuditName, o => o.ResolveUsing(s => s.Context.Options.GetRequestUserName()))
                .ForMember(m => m.AuditTime, o => o.ResolveUsing(s => s.Context.Options.GetRequestExecTime()));
            Mapper.AssertConfigurationIsValid();
            Mapper.Configuration.Seal();

            for (int i = 0; i < 10000; i++)
            {
                UserSetting us = new UserSetting {UserName = "Test", ExecTime = DateTime.Now};
                Quote q = new Quote {Id = i, Description = i.ToString("0000000")};
                Policy p = Mapper.Map<Policy>(q, o => o.Items[Constants.UserSetting] = us);
            }
        }

        private static void Test3()
        {
            Mapper.Reset();
            Mapper.CreateMap<Quote, Policy>()
                .ForMember(m => m.Id, o => o.Ignore())
                .ForMember(m => m.Number, o => o.Ignore())
                .ForMember(m => m.AuditName, o => o.ResolveUsing(s => s.Context.Options.GetCastRequestUserName()))
                .ForMember(m => m.AuditTime, o => o.ResolveUsing(s => s.Context.Options.GetCastRequestExecTime()));
            Mapper.AssertConfigurationIsValid();
            Mapper.Configuration.Seal();

            for (int i = 0; i < 10000; i++)
            {
                UserSetting us = new UserSetting {UserName = "Test", ExecTime = DateTime.Now};
                Quote q = new Quote {Id = i, Description = i.ToString("0000000")};
                Policy p = Mapper.Map<Policy>(q, o => o.Items[Constants.UserSetting] = us);
            }
        }

        private static void Test4()
        {
            Mapper.Reset();
            Mapper.CreateMap<Quote, Policy>()
                .ForMember(m => m.Id, o => o.Ignore())
                .ForMember(m => m.Number, o => o.Ignore())
                .ForMember(m => m.AuditName,
                    o =>
                        o.ResolveUsing<SingletonUserNameResolver>()
                            .ConstructedBy(() => SingletonUserNameResolver.Instance))
                .ForMember(m => m.AuditTime,
                    o =>
                        o.ResolveUsing<SingletonAuditTimeResolver>()
                            .ConstructedBy(() => SingletonAuditTimeResolver.Instance));
            Mapper.AssertConfigurationIsValid();
            Mapper.Configuration.Seal();    

            for (int i = 0; i < 10000; i++)
            {
                UserSetting us = new UserSetting {UserName = "Test", ExecTime = DateTime.Now};
                Quote q = new Quote {Id = i, Description = i.ToString("0000000")};
                Policy p = Mapper.Map<Policy>(q, o => o.Items[Constants.UserSetting] = us);
            }
        }
    }

    internal class UserSetting
    {
        public string UserName { get; set; }
        public DateTime ExecTime { get; set; }
    }

    public class Policy
    {
        public long Id { get; set; }
        public string Description { get; set; }

        public string Number { get; set; }
        public string AuditName { get; set; }
        public DateTime AuditTime { get; set; }
    }

    internal class Quote
    {
        public long Id { get; set; }
        public string Description { get; set; }

        public string AuditName { get; set; }
        public DateTime AuditTime { get; set; }
    }

    public static class Extensions
    {
        public static string GetRequestUserName(this MappingOperationOptions options)
        {
            // ReSharper disable once PossibleNullReferenceException
            return (options.Items[Constants.UserSetting] as UserSetting).UserName;
        }

        public static DateTime GetRequestExecTime(this MappingOperationOptions options)
        {
            // ReSharper disable once PossibleNullReferenceException
            return (options.Items[Constants.UserSetting] as UserSetting).ExecTime;
        }

        public static string GetCastRequestUserName(this MappingOperationOptions options)
        {
            // ReSharper disable once PossibleNullReferenceException
            return (options.Items.Values.Cast<UserSetting>().First()).UserName;
        }

        public static DateTime GetCastRequestExecTime(this MappingOperationOptions options)
        {
            // ReSharper disable once PossibleNullReferenceException
            return (options.Items.Values.Cast<UserSetting>().First()).ExecTime;
        }
    }

    public static class Constants
    {
        public const string UserSetting = "UserSetting";
    }

    public class UserNameResolver : IValueResolver
    {
        #region IValueResolver Members

        public ResolutionResult Resolve(ResolutionResult source)
        {
            return source.New((source.Context.Options.Items[Constants.UserSetting] as UserSetting).UserName);
        }

        #endregion
    }

    public class SingletonUserNameResolver : IValueResolver
    {
        private static Lazy<SingletonUserNameResolver> _instance =
            new Lazy<SingletonUserNameResolver>(() => new SingletonUserNameResolver());

        public static SingletonUserNameResolver Instance
        {
            get { return _instance.Value; }
        }

        #region IValueResolver Members

        public ResolutionResult Resolve(ResolutionResult source)
        {
            return source.New((source.Context.Options.Items[Constants.UserSetting] as UserSetting).UserName);
        }

        #endregion
    }

    public class AuditTimeResolver : IValueResolver
    {
        #region IValueResolver Members

        public ResolutionResult Resolve(ResolutionResult source)
        {
            return source.New((source.Context.Options.Items[Constants.UserSetting] as UserSetting).ExecTime);
        }

        #endregion
    }

    public class SingletonAuditTimeResolver : IValueResolver
    {
        private static Lazy<SingletonAuditTimeResolver> _instance =
            new Lazy<SingletonAuditTimeResolver>(() => new SingletonAuditTimeResolver());

        public static SingletonAuditTimeResolver Instance
        {
            get { return _instance.Value; }
        }

        #region IValueResolver Members

        public ResolutionResult Resolve(ResolutionResult source)
        {
            return source.New((source.Context.Options.Items[Constants.UserSetting] as UserSetting).ExecTime);
        }

        #endregion
    }
}

在Test1中,我使用自定义值解析器从Context.Options属性的Items字典中获取用户名和执行时间,标准审计字段。在Test2中,我绕过解析器并使用MappingOperationsOptions上的扩展方法直接跟踪字典。在Test3中,我也使用LINQ generic Cast进行相同的字典,知道UserSetting类型只有一个对象。

当使用RedGate的ANTS Profiler的分析协助运行时,Test3总共需要559.667ms,Test2需要565.783ms,而Test1需要1752.538ms。当我深入挖掘时,事实证明,每次迭代测试都会调用自定义值解析器的构造函数,每次迭代十万次。是否有可能避免那些代价高昂(生产场景中为1-4ms)的电话。还是我错过了一些明显的东西?

更新

首先,代码已更新,以实际执行映射。然后我添加了一个Singleton反模式,只构造一次自定义解析器。结果如下:

Test1 - 3189.186ms 
Test2 - 1297.870ms
Test3 - 1807.477ms
Test4 - 1381.149ms

因此字典上的扩展方法仍然是最快的,但是单例解析器紧随其后。

在具有24GB RAM和双核四核至强2.27GHz CPU的联想ThinkStation C20上执行测试。

更新2 我不确定我是否达到了Mr.Peer概述的性能基准测试标准,但是在将版本更改为Release并“预热”测试后,这里有新的结果:

    private static void Main(string[] args)
    {
        Mapper.Reset();
        Thread.Sleep(2000);
        Console.WriteLine("Warming up");
        RunTests();
        Console.WriteLine("Starting");
        for (int i = 0; i < 10; i++)
        {
            RunTests();
        }
        Console.WriteLine("Finished");
        Thread.Sleep(2000);
    }

    private static void RunTests()
    {
        Test1();
        Test2();
        Test3();
        Test4();
    }

结果(以ms为单位的儿童时间):

Test1 3405.485
Test2 1620.115
Test3 2005.364
Test4 1499.720

Visual Studio重启后

Test1 3654.204
Test2 1568.931
Test3 1901.894
Test4 2080.056

感谢我迄今为止收到的所有帮助。

1 个答案:

答案 0 :(得分:0)

所以,长话短说,在使用自定义解析器时要小心,如果需要,请应用Singleton反模式,或使用扩展方法来提高性能。