The application is built with DDD approach, with a separate set of persistence models. I called database object, or dbo:
public class ParentDbo
{
public int ParentId { get; set; }
public int TypeId { get; set; }
}
public class ChildDbo
{
public int ChildId { get; set; }
public ParentDbo Parent { get; set; }
public int RetryNumber { get; set; }
}
We have a simple model to look at: a parent and a child relationship. The RetryNumber presents the enum value in the database.
On retrieving data, it uses Dapper to first query the database, and use its splitOn feature to map data into them. This part is irrelevant but I will show it anyway for completeness:
const string sql = "SELECT * FROM XXX ....";
using (var cnt = _dbConnectionFactory.CreateConnection())
{
var childDbos = await cnt.QueryAsync<ChildDbo, ParentDbo, ChildDbo>(
sql: sql,
map: (childDbo, parentDbo) =>
{
childDbo.Parent = parentDbo;
return childDbo;
},
splitOn: "ParentId"
);
}
Dapper has limitation that it couldn't map data to private complex objects. That's mainly the reason why I have to have 2 sets of models. I would like to encapsulate the data and logic within domain models, with private setters and other techniques.
Here are my domain models:
public class Parent
{
public int Id { get; private set; }
public int TypeId { get; private set; }
public Parent(int parentId, int typeId)
{
// Validations
this.Id = parentId;
this.TypeId = typeId;
}
}
public class Child
{
public int Id { get; private set; }
public Parent Parent { get; private set; }
public Attempt Attempt { get; private set; }
public Child(int childId, Parent parent, Attempt attempt)
{
// Validations
this.Id = childId;
this.Parent = parent;
this.Attempt = attempt;
}
}
For domain models, I don't want public setters, and parameter-less constructors.
The Attempt is the enum with integer backing values:
public enum Attempt
{
Original = 1,
FirstRetry = 2,
SecondRetry = 3,
LastRetry = 4
}
Lastly, I want to use AutoMapper to map between Dbos and the domain models. Here is the mapping:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Child, ChildDbo>()
.ForMember(dest => dest.ChildId, opts => opts.MapFrom(src => src.Id))
.ForMember(dest => dest.RetryNumber, opts => opts.MapFrom(src => (int)src.Attempt))
.ReverseMap();
CreateMap<Parent, ParentDbo>()
.ForMember(dest => dest.ParentId, opts => opts.MapFrom(src => src.Id))
.ReverseMap();
}
}
I want to have two-ways mappings so I use ReverseMap().
.Net Fiddle demo: https://dotnetfiddle.net/saEHWd
It maps domain models to dbos without problem:
But its reverse, mapping from dbos to domain models, is throwing exceptions:
Unhandled exception. System.ArgumentException: Program+Child needs to have a constructor with 0 args or only optional args. (Parameter 'type')
at lambda_method18(Closure , Object , Child , ResolutionContext )
at AutoMapper.Mapper.MapCore[TSource,TDestination](TSource source, TDestination destination, ResolutionContext context, Type sourceType, Type destinationType, IMemberMap memberMap)
at AutoMapper.Mapper.Map[TSource,TDestination](TSource source, TDestination destination)
at AutoMapper.Mapper.Map[TDestination](Object source)
at Program.Main()
I've tried to remove the enum property and everything worked so I'm pretty sure it's the enum mapping that's having issues.
As far as I can see in your fiddle you are trying to map from ChildDbo to Parent and there is no mapping setup for it. Change the mapping code to:
var child2 = mapper.Map<Child>(childDbo);
And since there is mismatch in third Child's ctor param and source property names change map to:
CreateMap<Child, ChildDbo>()
.ForMember(dest => dest.ChildId, opts => opts.MapFrom(src => src.Id))
.ForMember(dest => dest.RetryNumber, opts => opts.MapFrom(src => (int)src.Attempt))
.ReverseMap()
.ConstructUsing((dbo, ctx) => new Child(dbo.ChildId, ctx.Mapper.Map<Parent>(dbo.Parent), (Attempt)dbo.RetryNumber));
See here
Or rename third Child's ctor parameter to retryNumber:
public Child(int childId, Parent parent, Attempt retryNumber)
see here.
or use ForCtorParam:
CreateMap<Child, ChildDbo>()
.ForMember(dest => dest.ChildId, opts => opts.MapFrom(src => src.Id))
.ForMember(dest => dest.RetryNumber, opts => opts.MapFrom(src => (int)src.Attempt))
.ReverseMap()
.ForCtorParam("attempt", opt => opt.MapFrom(dbo => dbo.RetryNumber))
Here.
Related
I have an object model something like this:
public class Concert {
public Band Band { get; set; }
public ConcertVenue Venue { get; set; }
}
public class TicketOrder {
public Concert Concert { get; set; }
public string CustomerName { get; set; }
}
// DTOs for email and web views:
public class ConcertDto {
public string Artist { get; set; }
public string Venue { get; set; }
}
public class TicketOrderDto : ConcertDto {
public string CustomerName { get; set; }
}
I'm using AutoMapper to map domain objects to DTOs. The DTOs here have an inheritance relationship that doesn't exist in the domain model (because when I send an email about a ticket order, I want to include all the information about the concert)
I have a mapping defined like this:
config.CreateMap<Concert, ConcertDto>()
.ForMember(dto => dto.Artist, opt => opt.MapFrom(concert => concert.Band.Name))
.ForMember(dto => dto.Venue, opt => opt.MapFrom(concert => concert.GetVenueSummary());
config.CreateMap<TicketOrder, ConcertDto>()
.ForMember(dto => dto.Artist, opt => opt.MapFrom(concert => concert.Band.Name))
.ForMember(dto => dto.Venue, opt => opt.MapFrom(concert => concert.GetVenueSummary())
.ForMember(dto => dto.CustomerName, optn.MapFrom(order => order.Customer.FullName))
;
There's some duplication in those maps, and what I want to do is to reuse the Concert > ConcertViewData mapping when I map the TicketOrderDto:
cfg.CreateMap<TicketOrder, TicketOrderDto>()
// This is what I *want* but isn't valid AutoMapper syntax:
.IncludeMembers(dto => dto, order => order.Concert)
.ForMember(dto => dto.CustomerName, optn.MapFrom(order => order.Customer.FullName));
but this fails with:
System.ArgumentOutOfRangeException: Only member accesses are allowed.
dto => dto (Parameter 'memberExpressions')
at AutoMapper.ExpressionExtensions.EnsureMemberPath(LambdaExpression exp, String name)
Calling .IncludeBase<> doesn't work, because ConcertOrder doesn't derive from Concert.
Is there an easy way to import one map into another but specify that it should map from a child object of the source type? i.e. "hey, please map source.Child onto this, and then run the regular source > this mapping?"
I am going to make an assumption here, but I believe the mapping should be from TicketOrder to TicketOrderDto, and not ConcertDto (which contains no CustomerName property) as the given models don't match the given mapping configuration.
In that case, you should be able to use .AfterMap() on ticket mapper configuration to map from Concert to ConcertDto.
cfg.CreateMap<TicketOrder, TicketOrderDto>()
.ForMember(d => d.CustomerName, o => o.MapFrom(s => s.Order.Customer.Name))
.AfterMap((s, d, context) => context.Mapper.Map(s.Concert, d));
I have issues on reverse mapping multiple properties back to complex objects, even with custom value resolvers.
Here are the persistence model:
public class EmailDbo
{
public int EmailId { get; set; }
public DateTime DateCreated { get; set; }
public DateTime? DateSent { get; set; }
public string SendTo { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
public bool DownloadAvailable { get; set; }
public DateTime? AdminDateSent { get; set; }
public string AdminEmail { get; set; }
public string AdminSubject { get; set; }
public string AdminBody { get; set; }
public int StatusId { get; set; }
}
I have Dapper map data from database and fill in this model.
Here are the domain models I want to map back and forth with the persistence model:
public class Email
{
public string SendTo { get; private set; }
public string Subject { get; private set; }
public string Body { get; private set; }
public DateTime? DateSent { get; private set; }
public Email(string sendTo, string subject, string body, DateTime? dateSent = null)
{
// Validations
this.SendTo = sendTo;
this.Subject = subject;
this.Body = body;
this.DateSent = dateSent;
}
}
public enum EmailTaskStatus
{
Sent = 1,
Unsent = 2
}
public class EmailTask
{
public int Id { get; private set; }
public DateTime DateCreated { get; private set; }
public Email PayerEmail { get; private set; }
public Email AdminEmail { get; private set; }
public bool DownloadAvailableForAdmin { get; private set; }
public EmailTaskStatus Status { get; private set; }
public EmailTask(int emailTaskId, DateTime dateCreated, Email payerEmail, Email adminEmail,
bool downloadAvailable, EmailTaskStatus status)
{
// Validations
this.Id = emailTaskId;
this.DateCreated = dateCreated;
this.PayerEmail = payerEmail;
this.AdminEmail = adminEmail;
this.DownloadAvailableForAdmin = downloadAvailable;
this.Status = status;
}
}
I would like to use a value object called Email for both the payer and admin email. You can tell they're just stored flatten in the database/persistence model. And the payer email is required but not the admin email.
I have the mapping configured like following:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<EmailTask, EmailDbo>()
.ForMember(dest => dest.EmailId, opts => opts.MapFrom(src => src.Id))
.ForMember(dest => dest.SendTo, opts => opts.MapFrom(src => src.PayerEmail.SendTo))
.ForMember(dest => dest.Subject, opts => opts.MapFrom(src => src.PayerEmail.Subject))
.ForMember(dest => dest.Body, opts => opts.MapFrom(src => src.PayerEmail.Body))
.ForMember(dest => dest.DateSent, opts => opts.MapFrom(src => src.PayerEmail.DateSent))
.ForMember(dest => dest.DownloadAvailable, opts => opts.MapFrom(src => src.DownloadAvailableForAdmin))
.ForMember(dest => dest.AdminEmail, opts =>
{
opts.PreCondition(src => (src.AdminEmail != null));
opts.MapFrom(src => src.AdminEmail.SendTo);
})
.ForMember(dest => dest.AdminSubject, opts =>
{
opts.PreCondition(src => (src.AdminEmail != null));
opts.MapFrom(src => src.AdminEmail.Subject);
})
.ForMember(dest => dest.AdminBody, opts =>
{
opts.PreCondition(src => (src.AdminEmail != null));
opts.MapFrom(src => src.AdminEmail.Body);
})
.ForMember(dest => dest.AdminDateSent, opts =>
{
opts.PreCondition(src => (src.AdminEmail != null));
opts.MapFrom(src => src.AdminEmail.DateSent);
})
.ForMember(dest => dest.StatusId, opts => opts.MapFrom(src => (int)src.Status))
.ReverseMap()
.ForCtorParam("status", opts => opts.MapFrom(src => src.StatusId))
.ForMember(dest => dest.PayerEmail, opts => opts.MapFrom<PayerEmailValueResolver>())
.ForMember(dest => dest.AdminEmail, opts => opts.MapFrom<AdminEmailValueResolver>());
}
}
After ReverseMap(), I want to grab multiple properties and construct the complex object Email. Hence I define two custom value resolvers for that:
public class PayerEmailValueResolver : IValueResolver<EmailDbo, EmailTask, Email>
{
public Email Resolve(EmailDbo emailDbo, EmailTask emailTask, Email email, ResolutionContext context)
{
return new Email(emailDbo.SendTo, emailDbo.Subject, emailDbo.Body, emailDbo.DateSent);
}
}
public class AdminEmailValueResolver : IValueResolver<EmailDbo, EmailTask, Email>
{
public Email Resolve(EmailDbo emailDbo, EmailTask emailTask, Email email, ResolutionContext context)
{
if (String.IsNullOrWhiteSpace(emailDbo.AdminEmail) &&
String.IsNullOrWhiteSpace(emailDbo.AdminSubject) &&
String.IsNullOrWhiteSpace(emailDbo.AdminBody) &&
!emailDbo.AdminDateSent.HasValue)
{
return null;
}
return new Email(emailDbo.SendTo, emailDbo.Subject, emailDbo.Body, emailDbo.DateSent);
}
}
As always, the mapping from the domain model to the Dbo works fine:
but not the other way, from Dbo to domain model. It's throwing exceptions:
Unhandled exception. System.ArgumentException: Program+EmailTask needs to have a constructor with 0 args or only optional args. (Parameter 'type')
at lambda_method32(Closure , Object , EmailTask , ResolutionContext )
at AutoMapper.Mapper.MapCore[TSource,TDestination](TSource source, TDestination destination, ResolutionContext context, Type sourceType, Type destinationType, IMemberMap memberMap)
at AutoMapper.Mapper.Map[TSource,TDestination](TSource source, TDestination destination)
at AutoMapper.Mapper.Map[TDestination](Object source)
.Net Fiddle demo: https://dotnetfiddle.net/DcTsPG
I wonder if AutoMapper confuses about those two Email objects: payer email and admin email, because they're both are Email type.
In reverse map AutoMapper is failing to create an instance of EmailTask.
Add a parameterless constructor to your EmailTask class -
public EmailTask()
{
// AutoMapper use only
}
Also, since your value resolvers are creating instance of Email, add a parameterless constructor to your Email class too -
public Email()
{
// AutoMapper use only
}
Finally, modify the PayerEmail and AdminEmail properties in EmailTask class so they can be set publicly -
public Email PayerEmail { get; set; }
public Email AdminEmail { get; set; }
That should solve your issue.
EDIT :
#David Liang, after reading your comment I'd say, to suit your scenario in light of DDD, you might need to modify your current mapping approach.
The thing is, when you are mapping EmailDbo from EmailTask, the process is easier because EmailDbo is a DTO type class with no parameterized constructor. Therefore, the property mapping only is enough to do the job.
But when you are trying to map EmailTask from EmailDbo, you are trying to instantiate a domain model class which has very strictly defined parameterized constructor that takes complex types as parameters, and is trying to protect how it's properties can and cannot be accessed from outside. Therefore, the .ReverseMap() approach you are using currently will not be very helpful, because the property mapping only will not be enough to provide you all the constructor parameters needed to instantiate the class. There's also AutoMapper's naming convention in the play.
Following is a mapping configuration for EmailTask from EmailDbo, where the reverse mapping is separated out and the value resolvers are refactored out into a helper class. The forward mapping remained unchanged.
CreateMap<EmailDbo, EmailTask>()
.ConstructUsing((s, d) =>
new EmailTask(
s.EmailId,
s.DateCreated,
Helper.GetPayerEmail(s),
Helper.GetAdminEmail(s),
s.DownloadAvailable,
(EmailTaskStatus)s.StatusId))
.IgnoreAllPropertiesWithAnInaccessibleSetter();
The Helper class -
public class Helper
{
public static Email GetPayerEmail(EmailDbo emailDbo)
{
return new Email(emailDbo.SendTo, emailDbo.Subject, emailDbo.Body, emailDbo.DateSent);
}
public static Email GetAdminEmail(EmailDbo emailDbo)
{
if (string.IsNullOrWhiteSpace(emailDbo.AdminEmail) && string.IsNullOrWhiteSpace(emailDbo.AdminSubject)
&& string.IsNullOrWhiteSpace(emailDbo.AdminBody) && !emailDbo.AdminDateSent.HasValue)
{
return null;
}
return new Email(emailDbo.SendTo, emailDbo.Subject, emailDbo.Body, emailDbo.DateSent);
}
}
Here is the complete fiddle - https://dotnetfiddle.net/2MxSdt
The context around this is that I'd like to map a dynamic dapper result to an object and that object has a property which has an interface type:
public class TargetModel
{
public int Id { get; set; }
public IAddress AbstractAddress { get; set; }
}
The dynamic to object bit is straight forward but I'm unable to configure AutoMapper to tell it how to handle the interface.
dynamic sourceModel = new ExpandoObject();
// flat model - id should map to TargetModel, Address01 will map to a nested type Address on TargetModel
sourceModel.Id = 1;
sourceModel.Address01 = "address01";
// for debugging purposes address maps ok from dynamic
Address address = Mapper.Map<Address>(sourceModel);
// this maps, but AbstractAddress is null - I need to config AutoMapper to understand how to map IAddress to Address
TargetModel target = Mapper.Map<TargetModel>(sourceModel);
I've tried to tell it how to deal with the property:
CreateMap<ExpandoObject, TargetModel>()
.ForMember(y => y.AbstractAddress, opts => opts.MapFrom(f => f));
Which fails with:
System.ArgumentException: Cannot create an instance of interface type
So I attempt to hint at the concrete:
CreateMap<IAddress, Address>().As<Address>();
Which doesn't resolve the issue and the exception remains.
I've looked at the following questions/concepts and tried various configuration options but haven't been able to get the map working:
AutoMapper: Mapping objects with interface properties
https://dotnetfiddle.net/fPQxWx
This is trying to map from a string in the ExpandoObject to an IAddress in your target type. Obviously it can't create an instance of IAddress to fill so you have to fulfill that yourself in the mapping.
If your models look like this:
public class TargetModel
{
public int Id { get; set; }
public IAddress AbstractAddress { get; set; }
}
public interface IAddress
{
string Address01 { get; set; }
}
public class Address : IAddress
{
public string Address01 { get; set; }
}
Then your config and setup can look like this:
public void MappingTests()
{
dynamic sourceModel = new ExpandoObject();
// flat model - id should map to TargetModel, Address01 will map to a nested type Address on TargetModel
sourceModel.Id = 1;
sourceModel.Address01 = "address01";
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ExpandoObject, TargetModel>()
.ForMember(dest => dest.AbstractAddress, opt => opt.MapFrom(src => new Address() { Address01 = src.First(kvp => kvp.Key == "Address01").Value.ToString() }))
.ForMember(destinationMember => destinationMember.Id, opt => opt.MapFrom(src => src.First(kvp => kvp.Key == "Id").Value));
});
TargetModel target = Mapper.Map<TargetModel>(sourceModel);
}
I just used it as an IEnumerable<KeyValuePair<string, object>>, but you can also treat the expando object like a dictionary by casting.
cfg.CreateMap<ExpandoObject, TargetModel>()
.ForMember(dest => dest.AbstractAddress, opt => opt.MapFrom(src => new Address() { Address01 = ((IDictionary<string, object>)src)["Address01"].ToString() }))
.ForMember(destinationMember => destinationMember.Id, opt => opt.MapFrom(src => ((IDictionary<string, object>)src)["Id"]));
I'm using automapper to map my entities. But entities have different structure.
Source:
public class SourceEntity
{
public string Name { get; set; }
public Type Type { get; set; }
public Communication SelectedCommunication { get; set; }
}
public enum Type
{
Type1=1,
Typ2
}
[Flags]
public enum Communication
{
Phone =1,
Email =2,
Post =4
}
Also I have HasFlag() extension method that will return true if flag is selected.
Destination entity:
public class DestinationEntity
{
public string Name { get; set; }
public bool Type1_PhoneSelected { get; set; }
public bool Type1_EmailSelected { get; set; }
public bool Type1_PostSelected { get; set; }
public bool Type2_PhoneSelected { get; set; }
public bool Type2_EmailSelected { get; set; }
public bool Type2_PostSelected { get; set; }
}
My map:
CreateMap<SourceEntity, DestinationEntity>()
.ForMember(v => v.Name, opt => opt.MapFrom(i => i.Name));
But I can't figure out the best way to map Types properties.
Is it possible to map it without typing something like:
.ForMemeber(v=>v.Test1_PhoneSelected, opt=>opt.MapFrom(i=>i.SelectedCommunication.HasFlag(Communication.Phone)))
.ForMemeber(v=>v.Test2_PhoneSelected, opt=>opt.MapFrom(i=>i.SelectedCommunication.HasFlag(Communication.Phone)))
For each of this properties.
Is there any way to map by naming convention?
Or any other ways?
You can use custom value resolvers
Although AutoMapper covers quite a few destination member mapping
scenarios, there are the 1 to 5% of destination values that need a
little help in resolving. Many times, this custom value resolution
logic is domain logic that can go straight on our domain. However, if
this logic pertains only to the mapping operation, it would clutter
our source types with unnecessary behavior. In these cases,
AutoMapper allows for configuring custom value resolvers for
destination members.
Example of custom value resolver:
public class YourCustomResolver
: IMemberValueResolver<object, object, Communication, bool>
{
private Communication _communication;
public YourCustomResolver(
Communication communication)
{
}
public bool Resolve(
object source,
object destination,
Communication sourceMember,
bool destMember,
ResolutionContext context)
{
return _communication == sourceMember;
}
}
Your mapping will look like this:
CreateMap<SourceEntity, DestinationEntity>()
.ForMember(dest => dest.Type1_PhoneSelected, opt => opt.ResolveUsing(new YourCustomResolver(Communication.Phone), src => src.SelectedCommunication))
.ForMember(dest => dest.Type1_EmailSelected, opt => opt.ResolveUsing(new YourCustomResolver(Communication.Email), src => src.SelectedCommunication))
.ForMember(dest => dest.Type1_PostSelected , opt => opt.ResolveUsing(new YourCustomResolver(Communication.Post) , src => src.SelectedCommunication))
.ForMember(dest => dest.Type2_PhoneSelected, opt => opt.ResolveUsing(new YourCustomResolver(Communication.Phone), src => src.SelectedCommunication))
.ForMember(dest => dest.Type2_EmailSelected, opt => opt.ResolveUsing(new YourCustomResolver(Communication.Email), src => src.SelectedCommunication))
.ForMember(dest => dest.Type2_PostSelected , opt => opt.ResolveUsing(new YourCustomResolver(Communication.Post) , src => src.SelectedCommunication));
I am in need to map the below scenario.
public class Customer
{
public string CustomerJson { get; set; }
}
public class CustomerTO
{
public object CustomerJson { get; set; }
}
From DAL I get CustomerJson value as below.
Customer.CustomerJson = {
"name": "Ram",
"city": "India"
}
I am in need to Deserialize this string. so I tried the below stuff while mapping.
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Customer, CustomerTO>()
.ForMember(dest => dest.CustName, opt => opt.MapFrom(src => JsonConvert.DeserializeObject(src.CustName)));
});
But this gives me run time error.
Unhandled Exception: AutoMapper.AutoMapperMappingException: Error mapping types.
So I kept it simple.
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Customer, CustomerTO>()
.ForMember(dest => dest.CustName, opt => opt.MapFrom(src => (src.CustName));
});
And I tried to deserialize it while consuming. But this give compile time error.
var custJson = JsonConvert.DeserializeObject(customerTO.CustomerJson );
Error 2 The best overloaded method match for 'Newtonsoft.Json.JsonConvert.DeserializeObject(string)' has some invalid arguments
I know customerTO.CustomerJson is not string but how do should I do the required mapping?
Thanks.
Based on your previous question and given the information above you seem to be confusing what you're trying to do here.
So I'm going to amalgamate the data from both in an attempt to solve the issues.
Entity Classes:
public class Customer
{
public int CustomerId {get; set; }
public string CustomerName { get; set; }
public string CustomerJson { get; set; }
}
public class CustomerTO
{
public int CustId { get; set; }
public object CustData { get; set; }
public object CustomerJson { get; set; }
}
AppMapper Class:
public static class AppMapper
{
public static MapperConfiguration Mapping()
{
return new MapperConfiguration(cfg =>
{
cfg.CreateMap<Customer, CustomerTO>()
.ForMember(dest => dest.CustId, opt => opt.MapFrom(src => src.CustomerId))
.ForMember(dest => dest.CustData, opt => opt.MapFrom(src => src.CustName))
.ForMember(dest => dest.CustomerJson, opt => opt.MapFrom(src => JsonConvert.DeserializeObject(src.CustomerJson));
});
}
}
Main:
public class Program
{
static void Main(string[] args)
{
var config = AppMapper.Mapping();
var mapper = config.CreateMapper();
// From Previous question get list of Customer objects
var customers = AddCustomers();
var mappedCustomers = mapper.Map<IEnumerable<CustomerTO>>(customers);
}
}
A couple of things to point out
I'm not sure what the purpose of CustData is in CustomerTO. It seems to be duplicating CustomerJson and if so remove it and the associated mapping.
Also, you never mention mapping from DTO back to entity, but for the JsonObject you just need to configure it to map the serialized string to the appropriate Property.
This is how I addressed my requirement.
Db Entity
public class Customer
{
public string CustomerData { get; set; }
// & other properties
}
My DTO
public class CustomerTO
{
public object CustomerData { get; set;}
// & other properties
}
I created a Utility like class with name AppMapper. This is how my AppMapper.cs looks like.
public class AppMapper
{
private IMapper _mapper;
public AppMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Customer, CustomerTO>();
//& other such mapping
});
_mapper = config.CreateMapper();
}
public CustomerTO Map(Customer customerEntity)
{
var customerTo= _mapper.Map<Customer,CustomerTO>(customerEntity);
return customerTo;
}
Now when I needed the mapping.
class DAL
{
public CustomerTO GetCustomers()
{
var customers= //LINQ To get customers
var customerTO = Mapping(customer);
return customerTO;
}
//However, this mapping glue in some internal class to retain SOLID principles
private CustomerTO Mapping(Customer custEntity)
{
var custTO = _appMapper.Map(custEntity);
var str = JsonConvert.Serialize(custTO.CustomerData);
custTO.CustomerData = JsonConvert.Deserialize(str);
return custTO;
}
}
That's it.
#Barry O'Kane - Sincere thanks for your inputs.
Points to be noted:-
I don't need to map manually any of the properites since the property name is same. Plus I am casting string to object. So no issues.
If you use .Map() for one property, then I found that I need to map each property else it gives default value of the data type.(Ex. for int it gives 0).
Yes. agreed there could be other method in Automapper which allows me specify that for a particulay property do this manual mapping & for rest use Automapping mechanism. But I am not sure on that.
Please feel free to improve this ans in any way.
Hope this helps :)