Skip to content

Commit ba53549

Browse files
PanyushkinDPanyushkinD
authored andcommitted
Add experimental support of Mapster
1 parent 1d14fc0 commit ba53549

11 files changed

Lines changed: 760 additions & 7 deletions

File tree

README.md

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
# AutoMapper.ExtendedConverters
2-
AutoMapper.ExtendedConverters is a small set of useful AutoMapper custom Type Converters
2+
AutoMapper.ExtendedConverters is a small set of useful [AutoMapper](https://github.com/AutoMapper/AutoMapper) (and [Mapster](https://github.com/eswann/Mapster)) custom Type Converters
33

44
__ListConverter__ and more general __CollectionConverter__ are usefull for updating data in existing collections.
55

66
For example, we have list of entites from database and it's modified version from client.
77
Then with `ListConverter` we can update all database entities from client objects with same `Id`.
88

9-
__CompiledConverter__ is more performant version of default AutoMapper converter for plain objects.
10-
It uses compiled Expressions behind the scenes. But it not supports custom conversion for specific properties.
11-
129
```cs
13-
using System.Diagnostics;
14-
using System.Linq;
1510
using AutoMapper;
1611
using AutoMapper.ExtendedConverters;
1712

@@ -75,3 +70,70 @@ class Program
7570
}
7671
}
7772
```
73+
74+
## Mapping collections other than `List<T>`
75+
```cs
76+
cfg.CreateMap<IEnumerable<Model>, ICollection<Entity>>()
77+
.UsingCollectionConverter((Model m) => m.Id, (Entity e) => e.Id);
78+
```
79+
80+
## Using with [Mapster](https://github.com/eswann/Mapster)
81+
82+
```cs
83+
using Mapster;
84+
using Mapster.CollectionChangeTracking;
85+
86+
class Model
87+
{
88+
public int Id { get; set; }
89+
public string Text { get; set; }
90+
}
91+
92+
class Entity
93+
{
94+
public int Id { get; set; }
95+
public string Text { get; set; }
96+
}
97+
98+
class Program
99+
{
100+
static void Configure()
101+
{
102+
TypeAdapterConfig<List<Model>, List<Entity>>.NewConfig()
103+
.TrackListChanges(m => m.Id, e => e.Id);
104+
105+
// for mapping collections other than lists
106+
TypeAdapterConfig<Model[], SortedSet<Entity>>.NewConfig()
107+
.TrackCollectionChanges((Model m) => m.Id, (Entity e) => e.Id);
108+
}
109+
110+
static void Main()
111+
{
112+
// values from Server
113+
var dest = new List<Entity>
114+
{
115+
new Entity { Id = 1, Text = "a" },
116+
new Entity { Id = 2, Text = "b" },
117+
};
118+
// values from Client
119+
var src = new List<Model>
120+
{
121+
new Model { Id = 1, Text = "A" },
122+
new Model { Id = 3, Text = "C" },
123+
};
124+
125+
List<Entity> res = TypeAdapter.Adapt(src, dest);
126+
127+
// now res is equivalent to
128+
new List<Entity>
129+
{
130+
// Entity with Id == 1 is updated
131+
new Entity { Id = 1, Text = "A" },
132+
// Entity with Id == 2 is removed
133+
// ...
134+
// Entity with Id == 3 is added
135+
new Entity { Id = 3, Text = "C" },
136+
}
137+
}
138+
}
139+
```

src/AutoMapper.ExtendedConverters.sln

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 14
4-
VisualStudioVersion = 14.0.24720.0
4+
VisualStudioVersion = 14.0.25420.1
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoMapper.ExtendedConverters", "AutoMapper.ExtendedConverters\AutoMapper.ExtendedConverters.csproj", "{8754F86B-0490-476B-97D3-1545F7938796}"
77
EndProject
88
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoMapper.ExtendedConverters.Tests", "AutoMapper.ExtendedConverters.Tests\AutoMapper.ExtendedConverters.Tests.csproj", "{0A6F3FD1-2836-4739-9164-1342A3955044}"
99
EndProject
1010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoMapper.ExtendedConverters.Benchmarks", "AutoMapper.ExtendedConverters.Benchmarks\AutoMapper.ExtendedConverters.Benchmarks.csproj", "{FCBD99EF-37BC-487F-B454-99C7A8286F6A}"
1111
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mapster.CollectionChangeTracking", "Mapster.CollectionChangeTracking\Mapster.CollectionChangeTracking.csproj", "{C65F5DA6-344B-4F67-9CFD-97FECB715014}"
13+
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mapster.CollectionChangeTracking.Tests", "Mapster.CollectionChangeTracking.Tests\Mapster.CollectionChangeTracking.Tests.csproj", "{23A00E22-A11E-4DA7-A3A2-0B0E26126D81}"
15+
EndProject
1216
Global
1317
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1418
Debug|Any CPU = Debug|Any CPU
@@ -27,6 +31,14 @@ Global
2731
{FCBD99EF-37BC-487F-B454-99C7A8286F6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
2832
{FCBD99EF-37BC-487F-B454-99C7A8286F6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
2933
{FCBD99EF-37BC-487F-B454-99C7A8286F6A}.Release|Any CPU.Build.0 = Release|Any CPU
34+
{C65F5DA6-344B-4F67-9CFD-97FECB715014}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35+
{C65F5DA6-344B-4F67-9CFD-97FECB715014}.Debug|Any CPU.Build.0 = Debug|Any CPU
36+
{C65F5DA6-344B-4F67-9CFD-97FECB715014}.Release|Any CPU.ActiveCfg = Release|Any CPU
37+
{C65F5DA6-344B-4F67-9CFD-97FECB715014}.Release|Any CPU.Build.0 = Release|Any CPU
38+
{23A00E22-A11E-4DA7-A3A2-0B0E26126D81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39+
{23A00E22-A11E-4DA7-A3A2-0B0E26126D81}.Debug|Any CPU.Build.0 = Debug|Any CPU
40+
{23A00E22-A11E-4DA7-A3A2-0B0E26126D81}.Release|Any CPU.ActiveCfg = Release|Any CPU
41+
{23A00E22-A11E-4DA7-A3A2-0B0E26126D81}.Release|Any CPU.Build.0 = Release|Any CPU
3042
EndGlobalSection
3143
GlobalSection(SolutionProperties) = preSolution
3244
HideSolutionNode = FALSE
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Microsoft.VisualStudio.TestTools.UnitTesting;
5+
6+
namespace Mapster.CollectionChangeTracking.Tests
7+
{
8+
[TestClass]
9+
public class CollectionChangeTrackingTests
10+
{
11+
public class Model
12+
{
13+
public int Id { get; set; }
14+
public string Text { get; set; }
15+
}
16+
17+
public class Entity : IComparable<Entity>
18+
{
19+
public int Id { get; set; }
20+
public string Text { get; set; }
21+
22+
public int CompareTo(Entity other)
23+
{
24+
return Id.CompareTo(other.Id);
25+
}
26+
}
27+
28+
[TestInitialize]
29+
public void Initialize()
30+
{
31+
TypeAdapterConfig<Model[], SortedSet<Entity>>.NewConfig()
32+
.TrackCollectionChanges((Model m) => m.Id, (Entity e) => e.Id);
33+
34+
TypeAdapterConfig<IEnumerable<Model>, LinkedList<Entity>>.NewConfig()
35+
.TrackCollectionChanges((Model m) => m.Id, (Entity e) => e.Id);
36+
37+
//TypeAdapterConfig<IEnumerable<Model>, ICollection<Entity>>.NewConfig()
38+
// .TrackCollectionChanges((Model m) => m.Id, (Entity e) => e.Id);
39+
}
40+
41+
[TestMethod]
42+
public void ShouldMapNullSource_WithoutDestination()
43+
{
44+
SortedSet<Entity> res = TypeAdapter.Adapt<Model[], SortedSet<Entity>>(null);
45+
46+
Assert.IsNull(res);
47+
}
48+
49+
[TestMethod]
50+
public void ShouldMapNullSource_ToExistingDestination()
51+
{
52+
var dest = new SortedSet<Entity> {
53+
new Entity { Id = 1, Text = "a" },
54+
new Entity { Id = 2, Text = "b" },
55+
new Entity { Id = 3, Text = "c" },
56+
};
57+
58+
SortedSet<Entity> res = TypeAdapter.Adapt<Model[], SortedSet<Entity>>(null, dest);
59+
60+
Assert.IsNull(res);
61+
}
62+
63+
[TestMethod]
64+
public void ShouldMapSource_WithoutDestination()
65+
{
66+
var src = new[] {
67+
new Model { Id = 1, Text = "A" },
68+
new Model { Id = 2, Text = "B" },
69+
new Model { Id = 3, Text = "C" },
70+
new Model { Id = 4, Text = "D" },
71+
};
72+
73+
SortedSet<Entity> res = TypeAdapter.Adapt<Model[], SortedSet<Entity>>(src);
74+
75+
Assert.IsNotNull(res);
76+
Assert.AreEqual(src.Length, res.Count);
77+
78+
Assert.IsTrue(src.Select(m => m.Id).SequenceEqual(res.Select(e => e.Id)));
79+
Assert.IsTrue(src.Select(m => m.Text).SequenceEqual(res.Select(e => e.Text)));
80+
}
81+
82+
[TestMethod]
83+
public void ShouldMapSource_ToExistingDestination()
84+
{
85+
var src = new[] {
86+
new Model { Id = 1, Text = "A" },
87+
new Model { Id = 2, Text = "B" },
88+
new Model { Id = 4, Text = "D" },
89+
new Model { Id = 5, Text = "E" },
90+
};
91+
92+
var dest = new SortedSet<Entity> {
93+
new Entity { Id = 1, Text = "a" },
94+
new Entity { Id = 2, Text = "b" },
95+
new Entity { Id = 3, Text = "c" },
96+
};
97+
98+
var destArray = new Entity[3];
99+
dest.CopyTo(destArray, 0);
100+
101+
SortedSet<Entity> res = TypeAdapter.Adapt(src, dest);
102+
103+
var resArray = new Entity[4];
104+
res.CopyTo(resArray, 0);
105+
106+
Assert.IsNotNull(res);
107+
Assert.AreEqual(src.Length, res.Count);
108+
109+
// should preserve objects with keys both in source and destination
110+
Assert.AreSame(destArray[0], resArray[0]);
111+
Assert.AreSame(destArray[1], resArray[1]);
112+
// should add objects with keys in source but not in destination
113+
Assert.IsNotNull(resArray[2]);
114+
Assert.IsNotNull(resArray[3]);
115+
// should remove objects with keys in destination but not in source
116+
Assert.AreNotSame(destArray[2], resArray[2]);
117+
Assert.AreNotSame(destArray[2], resArray[3]);
118+
119+
// should map values of collection items
120+
Assert.IsTrue(src.OrderBy(m => m.Id).Select(m => m.Id).SequenceEqual(res.Select(e => e.Id)));
121+
Assert.IsTrue(src.OrderBy(m => m.Id).Select(m => m.Text).SequenceEqual(res.Select(e => e.Text)));
122+
}
123+
124+
private static IEnumerable<Model> BuildSource()
125+
{
126+
yield return new Model { Id = 1, Text = "A" };
127+
yield return new Model { Id = 2, Text = "B" };
128+
yield return new Model { Id = 4, Text = "D" };
129+
yield return new Model { Id = 5, Text = "E" };
130+
}
131+
132+
public class ModelWrapper
133+
{
134+
public IEnumerable<Model> Collection { get; set; }
135+
}
136+
137+
public class EntityWrapper
138+
{
139+
public ICollection<Entity> Collection { get; set; }
140+
}
141+
142+
[TestMethod]
143+
public void ShouldMapAnyIEnumerable_ToAnyICollection()
144+
{
145+
var dest = new LinkedList<Entity>(new[] {
146+
new Entity { Id = 1, Text = "a" },
147+
new Entity { Id = 2, Text = "b" },
148+
new Entity { Id = 3, Text = "c" },
149+
});
150+
151+
var destArray = new Entity[3];
152+
dest.CopyTo(destArray, 0);
153+
154+
var destWrapper = new EntityWrapper { Collection = dest };
155+
var srcWrapper = new ModelWrapper { Collection = BuildSource() };
156+
157+
var res = TypeAdapter.Adapt(srcWrapper, destWrapper);
158+
159+
var resArray = new Entity[4];
160+
res.Collection.CopyTo(resArray, 0);
161+
162+
// should preserve collection type
163+
Assert.AreEqual(dest.GetType(), res.Collection.GetType());
164+
// should preserve objects with keys both in source and destination
165+
Assert.AreSame(destArray[0], resArray[0]);
166+
Assert.AreSame(destArray[1], resArray[1]);
167+
// should add objects with keys in source but not in destination
168+
Assert.IsNotNull(resArray[2]);
169+
Assert.IsNotNull(resArray[3]);
170+
// should remove objects with keys in destination but not in source
171+
Assert.AreNotSame(destArray[2], resArray[2]);
172+
Assert.AreNotSame(destArray[2], resArray[3]);
173+
174+
// should map values of collection items
175+
Assert.IsTrue(BuildSource().Select(m => m.Id).SequenceEqual(res.Collection.Select(e => e.Id)));
176+
Assert.IsTrue(BuildSource().Select(m => m.Text).SequenceEqual(res.Collection.Select(e => e.Text)));
177+
}
178+
}
179+
}

0 commit comments

Comments
 (0)