1

AutoMapperは、2つのオブジェクト間でプロパティをマップするためだけのものであることを私は知っています。

Automapperはデータを再形成するためのものではなく、これは私の意見ではプログラムする必要があります。

私はにPeriodDTO.IEnumerable<Period>形を変える必要があるを持っていますPeriodListViewModel.List<CellViewModel>

それぞれCellViewModelが保持しますList<RowViewModel>

これは1対1のマッピングではなく、おそらくこれはマッピングされるべきではないと思います-不可能なので-

public class PeriodRequest
    {
        public IEnumerable<Period> Periods { get; set; }
        public IEnumerable<DateTime> Weeks { get; set; }
    }

public class PeriodListViewModel
{       
    public PeriodListViewModel(IEnumerable<Period> periods)
    {
        CellViewModels = new List<CellViewModel>();
        var groupedPeriods = periods.GroupBy(p => p.LessonNumber).OrderBy(p => p.Key).ToList();
        foreach (var groupedPeriod in groupedPeriods)
        {
            var cellViewModel = new CellViewModel();
            CellViewModels.Add(cellViewModel);
            foreach (var period in groupedPeriod)
            {
                var rowViewModel = new RowViewModel();
                rowViewModel.Content = period.Content;
                rowViewModel.SchoolclassCode = period.SchoolclassCode;
                rowViewModel.DayName = period.LessonDate.ToShortDateString();
                rowViewModel.DayIndex = (int)period.LessonDate.DayOfWeek;
                rowViewModel.LessonNumber = period.LessonNumber;
                cellViewModel.Rows.Add(rowViewModel);
            }
        }
    }
    public List<CellViewModel> CellViewModels { get; set; }
}

public class CellViewModel
    {
        public CellViewModel()
        {
            Rows = new List<RowViewModel>();
        }
        public List<RowViewModel> Rows { get; set; }
    }

public class RowViewModel
    {
        public string DayName { get; set; }
        public string SchoolclassCode { get; set; }
        public string Content { get; set; }
        public int DayIndex { get; set; }
        public int LessonNumber { get; set; }
    }

public class Period
    {
        public int PeriodId { get; set; }
        public DateTime LessonDate { get; set; }
        public int LessonNumber { get; set; }
        public string SchoolclassCode { get; set; }
        public string Content { get; set; }
        public int SchoolyearId { get; set; }
        public Schoolyear Schoolyear { get; set; }
...
}

現時点では、PeriodListViewModelを簡単に単体テストできます。AutoMapperで状況をさらに改善するにはどうすればよいでしょうか。

4

2 に答える 2

1

IEnumerable<Period>からへの簡単な 1:1 マッピングはありませんが、例では と の間でList<CellViewModel>あるクラス間の接触を取得する場所でのみ Automapper を使用することで、Automapper から何らかの値を引き出すことができます。PeriodRowViewModel

Periodこれを念頭に置いて、 からへの再形成が必要なプロジェクションを設定すれば、LINQ をより有効に活用して、すべてを 1 回のスイープ モーションで実行することもできますRowViewModel

これを正確に示すコンソール コードの例を次に示します。Automapper のリゾルバー クラスの概念を使用して再形成を行っていることに注意してください。また、宣言を改善するのに役立つを受け入れるためにValueResolve<TSource,TDestination>、追加のコンストラクターを追加しました。CellViewModelIEnumerable<RowViewModel>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using AutoMapper;

namespace GroupingMapping
{
    public class PeriodRequest
    {
        public IEnumerable<Period> Periods { get; set; }
        public IEnumerable<DateTime> Weeks { get; set; }
    }

    public class RowViewModel
    {
        public string DayName { get; set; }
        public string SchoolclassCode { get; set; }
        public string Content { get; set; }
        public int DayIndex { get; set; }
        public int LessonNumber { get; set; }
    }

    public class Period
    {
        public int PeriodId { get; set; }
        public DateTime LessonDate { get; set; }
        public int LessonNumber { get; set; }
        public string SchoolclassCode { get; set; }
        public string Content { get; set; }
        public int SchoolyearId { get; set; }
        // No definition provided for Schoolyear
        //public Schoolyear Schoolyear { get; set; }
    }

    public class CellViewModel
    {
        public CellViewModel()
        {
            Rows = new List<RowViewModel>();
        }

        public CellViewModel(IEnumerable<RowViewModel> rowVMSet)
        {
            Rows = new List<RowViewModel>(rowVMSet);
        }

        public List<RowViewModel> Rows { get; set; }
    }

    public class PeriodListViewModelEx
    {
        public PeriodListViewModelEx(IEnumerable<Period> periods)
        {
            CellViewModels = new List<CellViewModel>(periods
                .GroupBy(p => p.LessonNumber)
                .OrderBy(grp => grp.Key)
                .Select(grp =>
                {
                    return new CellViewModel(
                        grp.Select(p => { return Mapper.Map<Period, RowViewModel>(p); }));
                }));
        }
        public List<CellViewModel> CellViewModels { get; set; }
    }

    class DateTimeToDateNameResolver : ValueResolver<DateTime, string>
    {
        protected override string ResolveCore(DateTime source)
        {
            return source.ToShortDateString();
        }
    }

    class DateTimeToDayOfWeekResolver : ValueResolver<DateTime, int>
    {
        protected override int ResolveCore(DateTime source)
        {
            return (int)source.DayOfWeek;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Mapper.CreateMap<Period, RowViewModel>()
                .ForMember(dest => dest.DayName, opt => opt.ResolveUsing<DateTimeToDateNameResolver>().FromMember(src => src.LessonDate))
                .ForMember(dest => dest.DayIndex, opt => opt.ResolveUsing<DateTimeToDayOfWeekResolver>().FromMember(src => src.LessonDate));

            Period[] periods = new Period[3];

            periods[0] = new Period { PeriodId = 1, LessonDate = DateTime.Today.Add(new TimeSpan(1, 0, 0, 0)), LessonNumber = 101, SchoolclassCode = "CS101", Content = "Intro to CS", SchoolyearId = 2013 };
            periods[1] = new Period { PeriodId = 2, LessonDate = DateTime.Today.Add(new TimeSpan(2, 0, 0, 0)), LessonNumber = 101, SchoolclassCode = "CS101", Content = "Intro to CS", SchoolyearId = 2013 };
            periods[2] = new Period { PeriodId = 3, LessonDate = DateTime.Today.Add(new TimeSpan(1, 0, 0, 0)), LessonNumber = 102, SchoolclassCode = "EN101", Content = "English (I)", SchoolyearId = 2013 };

            PeriodListViewModelEx pvModel = new PeriodListViewModelEx(periods);

            Console.WriteLine("CellViews: {0}", pvModel.CellViewModels.Count);

            foreach (CellViewModel cvm in pvModel.CellViewModels)
            {
                Console.WriteLine("{0} items in CellViewModel Group", cvm.Rows.Count);
            }

            Console.WriteLine("Inspecting CellViewModel Rows");
            foreach (CellViewModel cvm in pvModel.CellViewModels)
            {
                foreach (RowViewModel rvm in cvm.Rows)
                {
                    Console.WriteLine("  DayName: {0}", rvm.DayName);
                    Console.WriteLine("  SchoolclassCode: {0}", rvm.SchoolclassCode);
                    Console.WriteLine("  Content: {0}", rvm.Content);
                    Console.WriteLine("  DayIndex: {0}", rvm.DayIndex);
                    Console.WriteLine("  LessonNumber: {0}", rvm.LessonNumber);
                    Console.WriteLine("  -");
                }
                Console.WriteLine("--");
            }

            Console.ReadKey();
        }
    }
}
于 2013-02-27T22:02:51.233 に答える
0

数か月間、私は主にエンティティ モデルからビュー モデルを作成するために AutoMapper を使用しました。AutoMapper を使用して、非常に複雑なモデルを平坦化および非平坦化しました。

私は個人的には、ビューモデルのコンストラクターではそれを行いません。その理由は、PeriodListViewModel のソースが異なる可能性があるためです。アイデアは、マッパーを作成/構成したら、状況に応じて自由に実行できるということMapper.Map<PeriodViewModel(collectionOfCells);ですMapper.Map<PeriodViewModel(periodEntity);このようにして、PeriodViewModels も Period クラスも、互いに知識や関係を持ちません。 データベースの変更に応じて Period クラスが変更された場合、Automapper マッピングを調整して変更に対応するだけで、PeriodViewModel クラスは変更されないため、それを使用するビューを変更する必要はありません。その逆も同様です。

オートマッパーを使用していなかったとしても、静的ヘルパー メソッドを使用してエンティティ モデルをビュー モデルにコピーする必要がありました。ビュー モデルのような単純なものは、使いやすくするために単純な既定のコンストラクターを持つ必要があります。

ただし、AutoMapper で複雑なマッピングを行うのは非常に困難であり、デバッグは困難な場合があります。何度も別の開発者のオフィスで、発生している一連のマッピングを理解するのを手伝っていることに気づきました。その名前が示すように、子オブジェクト/コレクション/ナビゲーション プロパティを掘り下げると、それらが自動的にマップされるため、多くのことが暗黙のうちに発生し、デバッグが非常に困難になります。基本的に、これらのオブジェクト グラフを頭の中で視覚化してから、使用されていると思われるマッピングを探し出す必要があります。マッピング オプションの中には、複数行の匿名メソッドを許可しないものがあるため、ブレークポイントや追加のログ コードを配置することはできません。

データベースからフロントエンドにデータを頻繁にプッシュする開発者として、AutoMapper は私がいつも夢見ていたものとほとんど同じですが、数か月使用した後は、2 レベル以上の深さのオブジェクト グラフを変換するために使用することはおそらく避けたでしょう。あなたの例で行っていることについては、AutoMapper で十分だと思います。

アップデート:

クエリとマッピングを別々に行います。where 基準にもパラメーターを渡す必要がある場合があるため、エンティティを取得するための最初のクエリ。次に、ToList から得られた List が automapper.map に渡されて、Period エンティティが PeriodViewModel のエンティティに変換されます。あなたの場合、グループ化のためにネストされたコレクションがあります。Periods(groupedPeriod) のコレクションから単一の CellViewModel へのマッピングを作成してから、

foreach (var groupOfPeriods in groupedPeriods)
{
            Mapper.Map<CellViewModel>(groupOfPeriods );
            CellViewModels.Add(cellViewModel);
}

IGrouping は IEnumerable を実装し、この場合 TElement は Period.

もう 1 つのオプションは、IEnumerable から CellViewModel へのマッピングを作成する代わりに、Period から RowViewModel へのマッピングを作成し、これを行うことです。

...
foreach (var groupedPeriod in groupedPeriods)
{
    var cellViewModel = new CellViewModel();
    CellViewModels.Add(cellViewModel);
    foreach (var period in groupedPeriod)
    {
        var rowViewModel = Mapper.Map<RowViewModel>(period); 
        cellViewModel.Rows.Add(rowViewModel);    
    }
}
...

2 番目のオプションは、マッピングがより単純になるため、おそらくより単純なオプションになります。最初のオプションは、CreateMap にあまりにも多くの「魔法」が焼き付けられるため、保守/デバッグがより困難になる可能性があると思います。

ただし、古い自動マッパー コードが手元にないため、実際の CreateMap パーツの構成方法に関する正確なコード例を示すことはできませんが、2 番目のオプションはかなり簡単です。

また、automapper は、単一のアイテムをマップする方法を指示するだけで、もののリストをマップする方法を理解するので、おそらくこれでうまくいくでしょう:

...
foreach (var groupedPeriod in groupedPeriods)
{
    var cellViewModel = new CellViewModel();
    CellViewModels.Add(cellViewModel);
    cellViewModel.Rows = Mapper.Map<List<RowViewModel>>(groupedPeriod);
}
...

コレクションのタイプが正確に何であるかに依存しますRows。そうでない場合は、List<RowViewModel>その部分を変更して、AutoMapper が正しいタイプのコレクションを作成するようにします。マッピング リストの詳細については、https ://github.com/AutoMapper/AutoMapper/wiki/Lists-and-arrays を参照してください。

于 2013-02-27T21:32:36.280 に答える