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