[NuGetPackage] 使用 Mapster 處理物件對應

使用 Mapster 處理物件對應

平時都會使用AutoMapper做物件對應處理,這次拿新玩具Mapster玩玩

筆記一下做法


Install-Package Mapster

1.兩個屬性名稱相同的物件轉換

    public class ClassA
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public decimal Price { get; set; }

        public string Count { get; set; }
    }

    public class ClassB
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public decimal Price { get; set; }
    }


    public void BasicConvert()
    {
        var classA = new ClassA()
        {
            Id = 123,
            Name = "",
            Price = 23.3m
        };

        var classB = classA.Adapt<ClassB>();
        classB.Should().BeEquivalentTo(new ClassB()
        {
            Id = 123,
            Name = "",
            Price = 23.3m
        });
    }

2.屬性名稱相同但型別不同的物件

    public class ClassA
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public decimal Price { get; set; }

        public string Count { get; set; }
    }

    public class ClassC
    {
        public string Id { get; set; }

        public string Name { get; set; }

        public string Price { get; set; }

        public int Count { get; set; }
    }

    public void BasicConvert_Type_Not_Same()
    {
        var classA = new ClassA()
        {
            Id = 123,
            Name = "",
            Price = 23.3m,
            Count = "3",
        };

        var classC = classA.Adapt<ClassC>();
        classC.Should().BeEquivalentTo(new ClassC()
        {
            Id = "123",
            Name = "",
            Price = "23.3",
            Count = 3,
        });
    }

3.從字典轉換為物件

    public class ClassA
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public decimal Price { get; set; }

        public string Count { get; set; }
    }

    public void BasicConvert_Dictionary()
    {
        var dictionary = new Dictionary<string, string>()
        {
            { "Id", "1" },
            { "Name", "Test" }
        };

        var classA = dictionary.Adapt<ClassA>();
        classA.Should().BeEquivalentTo(new ClassA()
        {
            Id = 1,
            Name = "Test"
        });
    }

4.集合的轉換

    public class ClassA
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public decimal Price { get; set; }

        public string Count { get; set; }
    }

    public class ClassB
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public decimal Price { get; set; }
    }

    public void BasicConvert_List()
    {
        var classAs = new List<ClassA>()
        {
            new ClassA(){Id = 1,Name = "Test"},
            new ClassA(){Id = 2,Name = "Test2"},
        };

        var classBs = classAs.Adapt<List<ClassB>>();
        classBs.Should().BeEquivalentTo(new List<ClassB>()
        {
            new ClassB() { Id = 1, Name = "Test" },
            new ClassB() { Id = 2, Name = "Test2" },
        });
    }

5.從靜態類別自定義轉換邏輯

    public void CustomConvert()
    {
        TypeAdapterConfig<ClassA, ClassB>
            .NewConfig()
            .Ignore(b => b.Price)
            .Map(b => b.Name, a => $"{a.Id}_{a.Name}");
            
        var classA = new ClassA()
        {
            Id = 1,
            Name = "Test",
            Price = 12.34m
        };
        var classB = classA.Adapt<ClassB>();
            
        classB.Should().BeEquivalentTo(new ClassB()
        {
            Id = 1,
            Name = "1_Test",
            Price = 0m,
        });
    }

6.包含子物件的物件轉換

    public class ClassD
    {
        public int Id { get; set; }

        public ClassB ClassB { get; set; }
    }

    public class ClassB
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public decimal Price { get; set; }
    }

    public class MyClassA
    {
        public int Id { get; set; }

        public MyClassB ClassB { get; set; }
    }

    public class MyClassB
    {
        public int Id { get; set; }

        public string Name { get; set; }
    }

    public void InnerClass()
    {
        TypeAdapterConfig<ClassB, MyClassB>.ForType();
        var classD = new ClassD
        {
            Id = 1,
            ClassB = new ClassB
            {
                Id = 2,
                Name = "Test",
                Price = 23m
            }
        };
        var myClassA = classD.Adapt<MyClassA>();
        myClassA.Should().BeEquivalentTo(new MyClassA()
        {
            Id = 1,
            ClassB = new MyClassB()
            {
                Id = 2,
                Name = "Test"
            }
        });
     }

7.從instance config自定義轉換邏輯

    public class ClassA
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public decimal Price { get; set; }

        public string Count { get; set; }
    }

    public class ClassB
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public decimal Price { get; set; }
    }

    public void Basic()
    {
        var config = new TypeAdapterConfig();
        config.NewConfig<ClassA,ClassB>()
              .Map(b=>b.Id,a=>a.Id+1)
              .Map(b=>b.Name,a=>"Name");

        var classA = new ClassA()
        {
            Id = 1,
            Name = "Test"
        };
        var classB = classA.Adapt<ClassB>(config);
        classB.Should().BeEquivalentTo(new ClassB()
        {
            Id = 2,
            Name = "Name"
        });
    }

8.從ASP.NET Core DI框架注入

Install-Package Mapster.DependencyInjection
Startup.cs

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();

            services.AddSingleton(GetAdapterConfig());
            services.AddScoped<IMapper, ServiceMapper>();
        }

        private TypeAdapterConfig GetAdapterConfig()
        {
            var config = new TypeAdapterConfig();

            config.NewConfig<ClassA, ClassB>()
                  .Map(b => b.Name, a => $"{a.Id}_{a.Price}");

            config.NewConfig<ClassB, ClassC>()
                  .Ignore(c => c.Name)
                  .Map(c => c.Id, b => b.Id + 2);

            return config;
        }

9.注入使用

Controller

        public HomeController(IMapper mapper)
        {
            _mapper = mapper;
        }

        public string Index()
        {
            var classA = new ClassA()
            {
                Id = 1,
                Name = "Test",
                Price = 12.34m
            };
            var classB = _mapper.Map<ClassB>(classA);

            return JsonConvert.SerializeObject(classB);
        }

        public string Index2()
        {
            var classA = new ClassB()
            {
                Id = 1,
                Name = "Test",
                Price = 12.34m
            };
            var classC = _mapper.Map<ClassC>(classA);

            return JsonConvert.SerializeObject(classC);
        }

官方也有提供常見幾個套件的效能比較,跟主流的AutoMapper相比不差

Method Mean StdDev Error Gen 0 Gen 1 Gen 2 Allocated
'Mapster 5.0.0' 141.84 ms 0.931 ms 1.408 ms 31250.0000 - - 125.12 MB
'Mapster 5.0.0 (Roslyn)' 60.48 ms 1.186 ms 1.993 ms 31222.2222 - - 125.12 MB
'Mapster 5.0.0 (FEC)' 58.17 ms 0.231 ms 0.442 ms 29714.2857 - - 119.02 MB
'Mapster 5.0.0 (Codegen)' 51.56 ms 0.312 ms 0.524 ms 31200.0000 - - 125.12 MB
'ExpressMapper 1.9.1' 299.05 ms 2.081 ms 3.146 ms 60000.0000 - - 241.85 MB
'AutoMapper 9.0.0' 708.06 ms 3.398 ms 5.137 ms 91000.0000 - - 364.69 MB

GitHub https://github.com/MapsterMapper/Mapster

Sample Code https://github.com/ianChen806/MapsterSample/tree/master