web-dev-qa-db-ja.com

オートマッパーを高速化する必要があります... 113個のオブジェクトを実行するには32秒かかります

こんにちは私は自動マッパーにいくつかの大きな問題があり、それが遅いです。どうすればスピードアップできるかわかりません。

Nhibernate、fluent nhibernate、およびasp.net mvc3.0を使用しています

[Serializable()]
    public class Test
    {
        public virtual int Id { get; private set; }
        public virtual string Name { get;  set; }
        public virtual string Description { get; set; }
        public virtual DateTimeDate { get; set; }
        public virtual IList<Reminder> Reminders { get; set; }
        public virtual IList<Reminder2> Reminders2 { get; set; }
        public virtual Test2 Test2 { get; set; }

        public Test()
        {
            Reminders = new List<Reminders>();
            Reminders2 = new List<Reminders2>();
        }

    }

ご覧のとおり、いくつかのプロパティを取得しました。データベースにあるような他のクラスには、それらの間に参照があります。

次にこれを行います

var a = // get all items (returns a collection of Test2)
var List<MyViewModel> collection = new List<MyViewModel>();
     foreach (Test2 t in a)
            {
                MyViewModel vm = Mapper.Map<Test2, MyViewModel>(t);
                vm.SetDateFormat(t.DateTimeDate, DateFilters.All.ToString());

                collection.Add(vm);
            }

//モデルを表示

    public class MyViewModel
        {
            public int Id  { get; private set; }
            public string Name { get; set; }
            public string Description { get; set; }
            public DateTime DateTimeDate { get; set; }
            public string FormatedDueDate { get; set; }
            public string Test2Prefix { get; set; }
            public string Test2BackgroundColor { get; set; }
            public string SelectedDateFilter { get; set; }
            public bool DescState { get; set; }
            public bool AlertState { get; set; }


            /// <summary>
            /// Constructor
            /// </summary>
            public MyViewModel()
            {
                // Default values
                SelectedDateFilter = "All";
                DescState = false;
                AlertState = false;
            }

            /// <summary>
            /// Sets the date formatter string used
            /// </summary>
            /// <param name="dateFormat"></param>
            public void SetDateFormat(DateTime dueDate, string dateFilter)
            {
                // simple if statement to format date.
            }
        }

//マッピング

  Mapper.CreateMap<Test2,MyViewModel>().ForMember(dest => dest.DescState, opt =>
 opt.ResolveUsing<DescStateResolver>())
                 .ForMember(dest => dest.AlertState, opt =>
 opt.ResolveUsing<AlertStateResolver>());

//リゾルバ

public class AlertStateResolver : ValueResolver<Task, bool>
    {
        protected override bool ResolveCore(Task source)
        {
            if (source.Reminders.Count > 0 || source.Reminders2.Count > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }   

  public class DescStateResolver : ValueResolver<Task,bool>
    {
        protected override bool ResolveCore(Task source)
        {
            if (String.IsNullOrEmpty(source.Description))
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    }

奇妙な名前やタイプミスは無視してください。私の実際のオブジェクトは問題なく機能し、意味があります。

だから私はストップウォッチを使ってこれをしました

Stopwatch a = new Stopwatch()
    foreach (Test2 t in a)
                {
                    a.Start()                     
                    MyViewModel vm = Mapper.Map<Test2, MyViewModel>(t);
                    a.Stop()
                    vm.SetDateFormat(t.DateTimeDate, DateFilters.All.ToString());

                    collection.Add(vm);
                }

var b = a.Elapsed; // comes back with 32 seconds.

私はこれを非常にひどく最適化する必要があります。

26
chobo2

の代わりに:

var a = // get all items (returns a collection of Test2)
List<MyViewModel> collection = new List<MyViewModel>();
foreach (Test2 t in a)
{
    MyViewModel vm = Mapper.Map<Test2, MyViewModel>(t);
    vm.SetDateFormat(t.DateTimeDate, DateFilters.All.ToString());
    collection.Add(vm);
}

試してください:

var a = // get all items (returns a collection of Test2)
List<MyViewModel> collection = Mapper.Map<IEnumerable<Test2>, IEnumerable<MyViewModel>>(a);

これは、マッピング定義で実行できるSetDateFormat呼び出しを除いて、最初の呼び出しと同等です。それはまたより速いかもしれません。

Test2 => MyViewModelの間にマッピングが定義されている場合、AutoMapperは自動的にIEnumerable<Test2> => IEnumerable<MyViewModel>のマッピングを提供するため、ループする必要はありません。

また、あなたはあなたの質問でNHibernateについて言及しました。ソースオブジェクトとそのコレクションがデータベースから熱心にロードされていることを確認してくださいbeforeマッピングレイヤーに渡すか、AutoMapperがあなたのコレクションの1つをマッピングしようとするときに遅いと非難することはできませんNHibernateがこのコレクションをフェッチしなかったため、データベースにヒットするソースオブジェクト。

26
Darin Dimitrov

もう1つ探す必要があるのは、例外をスローするマッピングコードです。 AutoMapperはこれらをサイレントにキャッチしますが、この方法で例外をキャッチするとパフォーマンスに影響します。

したがって、SomethingThatMightBeNullがnullであることが多い場合、このマッピングはNullreferenceExceptionsが原因でパフォーマンスが低下します。

.ForMember(dest => dest.Blah, c.MapFrom(src=>src.SomethingThatMightBeNull.SomeProperty))

このような変更を加えると、マッピングにかかる​​時間の半分以上になることがわかりました

.ForMember(dest => dest.Blah, c.MapFrom(src=> (src.SomethingThatMightBeNull == null
    ? null : src.SomethingThatMightBeNull.SomeProperty)))

更新:C#6構文

.ForMember(dest => dest.Blah, c.MapFrom(src => (src.SomethingThatMightBeNull?.SomeProperty)))
28
AaronLS

これを追加すると、起動時間を改善できました

 .ForAllMembers(options => options.Condition(prop => prop.SourceValue != null));

それぞれの終わりに

.CreateMap<..,..>()
12
a.boussema

サブコレクションが大きい場合は、「Count> 1」の代わりに「Any()」を使用すると便利な場合があります。 Any関数は1回だけ繰り返す必要がありますが、Countはentmesコレクションを繰り返す必要がある場合があります(実装によって異なります)。

3
TheNameless

これが問題の原因であるかどうかはわかりませんが、自動実装されたプロパティのシリアル化には注意してください。

コードがコンパイルされるたびに、各(匿名の)バッキングフィールドの名前がコンパイラーによってランダムに選択されます。そのため、一度にコンパイルされたプログラムでデータをシリアル化し、別のプログラムで逆シリアル化すると、いくつかの驚くべき例外が発生する場合があります。

1
Chris Bednarski

私はあなたと同じ問題を修正しました。また、オブジェクトを1つだけマッピングするのに32秒かかります。したがって、opts.Ignore()を使用して、以下のようにカスタマイズされたオブジェクトを処理します。

            CreateMap<SiteConfiguration, Site>()
                .ForMember(x => x.SubSystems, opts => opts.Ignore())
                .ForMember(x => x.PointInformations, opts => opts.Ignore())
                .ForMember(x => x.Schedules, opts => opts.Ignore())
                .ForMember(x => x.EquipmentDefinitions, opts => opts.Ignore());

その後、それはほんの数ミリ秒かかります。

0
capcom923

良いヒントは、AutoMapperの構成を最適化し、ViewModelのプロパティにIgnoreを使用して、マッピングを検証するメソッド呼び出しを作成することです"Mapper.AssertConfigurationIsValid()"

Mapper.Initialize(cfg =>
        {
            cfg.ValidateInlineMaps = true;
            cfg.AllowNullCollections = false;
            cfg.AllowNullDestinationValues = true;                
            cfg.DisableConstructorMapping(); // <= In the case of my project, I do not use builders, I had a performance gain.
            cfg.AddProfile<DomainToViewModelMappingProfile>();
            cfg.AddProfile<ViewModelToDomainMappingProfile>();
        });
Mapper.AssertConfigurationIsValid();
0
Jean Gatto