In Automapper, we normally have 1 to 1 mapping defined but I have a
case whereas the incoming stream as a json payload which then I cast it
as a dynamic (using JObject parse) and in one of the property within the
payload it defined which object that it needs to cast into. Lets take a
look at the sample below
Input
Json payload to create a city
at the same time we can also have a Json payload to create a staff
So what we are doing in here, all the message will go into payload property (it can contain any object) and we add some extra information/header/metadata on the parent level
Desired Outputs
To map this to a concrete class 1:1 mapping is straight forward and
easy. The problem here is that the “messageType” is the one that decided
which object that it should be
Automapper Configuration:
1. POCO object
abstract class that stores all the metadata
2. Lets create a TypeConverter for the base class which is Metadata and from this converter it will return the derived class
3. Lets create a TypeConverter for the derived class which are Staff and City
3. Define the Automapper mapping in the configuration
Input
Json payload to create a city
1
2
3
4
5
6
7
8
9
10
11
| { "requestId": "C4910016-C30D-415C-89D3-D08D724429A6", "messageType": "CITY_CREATED", "categoryName": "categoryA", "metadata": { "city": "sydney", "state": "NSW", "postcode": "2000", "country": "australia" }} |
1
2
3
4
5
6
7
8
9
10
11
12
| { "requestId":"C4910016-C30D-415C-89D3-D08D724429A6", "messageType": "STAFF_CREATED", "categoryName": "categoryB", "staffDetail": { "name": "fransiscus", "dateOfBirth": "01/01/1950" }, "location" : { "cityId" : "1" }} |
Desired Outputs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| { "messageType": "CITY_CREATED", "payload": { "city": "sydney", "state": "NSW", "postcode": "2000", "country": "australia" }, "provider": "abc", "providerRequestId": "C4910016-C30D-415C-89D3-D08D724429A6", "receivedAt": "2015-09-30T23:53:58.6118521Z", "lastUpdated": "2015-09-30T23:53:58.6128283Z", "lastUpdater": "Transformer", "attempt": 0} |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| { "messageType": "STAFF_CREATED", "payload": { "staffName": "fransiscus", "dateOfBirth": "01/01/1950", "cityId": "1" }, "provider": "abc", "providerRequestId": "C4910016-C30D-415C-89D3-D08D724429A6", "receivedAt": "2015-09-30T23:53:58.6118521Z", "lastUpdated": "2015-09-30T23:53:58.6128283Z", "lastUpdater": "Transformer", "attempt": 0} |
Automapper Configuration:
1. POCO object
abstract class that stores all the metadata
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| public abstract class Metadata { public string MessageType { get; set; } public string Provider { get; set; } public string ProviderRequestId { get; set; } public DateTime ReceivedAt { get; set; } public DateTime LastUpdated { get; set; } public string LastUpdater { get; set; } public int Attempt { get; set; } public List<string> Errors { get; set; } } |
1
2
3
4
5
6
7
| public class City { public string CityName { get; set; } public string State { get; set; } public string PostCode { get; set; } public string Country { get; set; } } |
1
2
3
4
5
6
| public class StaffDetail { public string Name { get; set; } public string DateOfBirth { get; set; } public int CityId { get; set; } } |
1
2
3
4
| public class Message<T> : Metadata where T : class { public T Payload { get; set; } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| public class MetadataTypeConverter : TypeConverter<dynamic, Metadata> {protected override Metadata ConvertCore(dynamic source) { Metadata metadata; var type = (string)source.messageType.Value; switch (type) { case "STAFF_CREATED": metadata = new Message<StaffDetail> { Payload = Mapper.Map<dynamic, StaffDetail>(source) }; break; case "CITY_CREATED": metadata = new Message<City> { Payload = Mapper.Map<dynamic, City>(source) }; break; default: throw new Exception(string.Format("no mapping defined for {0}", source.messageType.Value)); } metadata.ProviderRequestId = source.requestId; metadata.Topic = string.Format("{0}.{1}.pregame", producerTopicName, source.categoryName ?? source.competition.categoryName); metadata.Provider = "My Provider"; metadata.MessageType = source.messageType; metadata.ReceivedAt = DateTime.UtcNow; metadata.LastUpdated = DateTime.UtcNow; metadata.LastUpdater = "Transformer"; metadata.Attempt = 0; return metadata; } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
| public class CityTypeConverter : TypeConverter<dynamic, City> { protected override City ConvertCore(dynamic source) { City city = new City(); city.CityName = source.metadata.city; city.State = source.metadata.state; city.Postcode = source.metadata.postcode; city.Country = source.metadata.country; return city; } } |
1
2
3
4
5
6
7
8
9
10
11
12
| public class StaffDetailTypeConverter : TypeConverter<dynamic, StaffDetail> { protected override StaffDetail ConvertCore(dynamic source) { StaffDetail staffdetail = new StaffDetail(); staffdetail.Name = source.staffDetail.name; staffdetail.DateOfBirth = source.staffDetail.dateOfBirth; staffdetail.CityId = source.location.cityId; return staffdetail; } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| public class WhafflMessageMapping : Profile { public override string ProfileName { get { return this.GetType().Name; } } protected override void Configure() { this.CreateMap() .ConvertUsing(new MetadataTypeConverter()); this.CreateMap() .ConvertUsing(new StaffDetailTypeConverter()); this.CreateMap() .ConvertUsing(new CityTypeConverter()); } private Metadata BuildWhafflMessage(dynamic context) { var type = ((string)context.messageType.Value); switch (type) { case "STAFF_CREATED": return new Message { Payload = Mapper.Map(context) }; case "CITY_CREATED: return new Message { Payload = Mapper.Map(context) }; default: throw new Exception(string.Format("no mapping defined for {0}", context.messageType.Value)); } } } |
No comments:
Post a Comment