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通用Cast,因为它知道只有一个UserSetting类型的对象。

在RedGate的ANTS Profiler的性能分析帮助下运行时,Test3总共花费了559.667毫秒,Test2花费了565.783毫秒,而Test1花费了1752.538毫秒。当我深入研究时,事实证明,每次测试迭代都会调用自定义值解析程序的构造函数,每次迭代十万次。有没有可能避免那些昂贵的电话(在生产场景中为1-4毫秒)。还是我缺少明显的东西?

更新

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

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

因此,字典上的扩展方法仍然是最快的,但单例解析器仅次于第二名。

测试是在具有24GB RAM和双四核Xeon 2.27GHz CPU的Lenovo ThinkStation C20上执行的。

更新2不确定我是否满足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();
    }

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

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

感谢您到目前为止所获得的所有帮助。

达雷克

因此,长话短说,使用自定义解析器时要小心,必要时应用Singleton反模式或使用扩展方法来提高性能。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章