LoginSignup
4
10

More than 1 year has passed since last update.

[C#] DapperでListプロパティを持つクラスに一発でマッピングさせる方法

Last updated at Posted at 2018-06-09

例えば

もし、以下のようなメンバークラスがあったとして
(説明のためにしょっぱくなっていますがご勘弁ください!!)

    public class Member
    {
        public int Id { get; set; }
        public string Name { get; set; }
        
        public string ClassCode { get; set; }
        public string ClassName { get; set; }
        
        public string GradeCode { get; set; }
        public string GradeName { get; set; }
    }

それを以下のようなListプロパティを持つ学年、組、生徒に一発でマッピングさせたい場合、この記事はお役に立てると思います。

    public class Grade
    {
        public string GradeCode { get; set; }
        public string GradeName { get; set; }
        public List<Class> Classes { get; set; }
    }

    public class Class
    {
        public string ClassCode { get; set; }
        public string ClassName { get; set; }
        public List<Student> Students { get; set; }
   }

    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

やり方

まずは完成形をご覧ください。

var grades = new List<Grade>();  // 最終的にこのListにマッピングします

using (var db = new MyDbContext())
{
    db.Database.Connection.Query<Student, Class, Grade, Student>(
        "select * from members",
        (s, c, g) =>
        {
            if (!grades.Contains(g))
            {
                c.Students = new List<Student>() { s };
                g.Classes = new List<Class>() { c };
                grades.Add(g);
                return s;
            }

            Grade grade = grades.Single(gl => gl.Equals(g));
            if (!grade.Classes.Contains(c))
            {
                c.Students = new List<Student>() { s };
                grade.Classes.Add(c);
                return s;
            }

            Class clazz = grade.Classes.Single(cl => cl.Equals(c));
            clazz.Students.Add(s);

            return s;
        },
        splitOn: "ClassCode,GradeCode");
}
  • ポイント
    1. Dapperの戻り値でマップさせられないので、内部のFuncでコネコネして作成する
    2. Func内のreturnはダミーで意味はありません
    3. Equalメソッドをoverrideして等価判定を簡潔に表現する(本題とは直接は関係ないですが)

ポイント3のサンプルは以下の通りです。

    public class Grade
    {
        public string GradeCode { get; set; }
        public string GradeName { get; set; }
        public List<Class> Classes { get; set; }

        // コードと名称の値が一致したら等価と判定する
        public override bool Equals(object obj)
        {
            if (obj == null || GetType() != obj.GetType())
                return false;

            Grade g = (Grade)obj;
            return (GradeCode == g.GradeCode) && (GradeName == g.GradeName);
        }

        // Equalsをoverrideすると併せて書かないと警告がでます
        // 同じくコードと名称のみハッシュ対象とします
        public override int GetHashCode()
        {
            var hashCode = 780226712;
            hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(GradeCode);
            hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(GradeName);
            return hashCode;
        }
    }

具体例

  • Memberデータ

image.png

  • 結果
foreach (var grade in grades)
{
    Console.WriteLine("------------");
    Console.WriteLine(grade.GradeName);
    var classes = grade.Classes.Select(c => c);
    foreach (var clazz in classes)
    {
        Console.WriteLine("--------");
        Console.WriteLine(clazz.ClassName);
        var students = clazz.Students.Select(s => s);
        foreach (var student in students)
        {
            Console.WriteLine(student.Name);
        }
    }
}

image.png

意図した通りにマッピングさせられました。

参考サイト

How do I map lists of nested objects with Dapper

本ソースをgithubに公開しております。(しょっぱいです。)
githubサンプルソース

4
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
10