Using Automapper with Flags Enum - c#

I'm trying to figure out how to use AutoMapper with the following scenario :-
I have the following Entity Model :-
public class Lender : LegacyEntity
{
public int Id { get; set; }
public string Name { get; set; }
public ClaimType ClaimTypes { get; set; }
//Other properties kepy out for brevity.
}
And Here is The Following Service Model :-
public class LenderServiceModel
{
[Editable(false)]
public int Id { get; set; }
[Editable(false)]
public string Name { get; set; }
[Editable(false)]
public List<string> ClaimTypes { get; set; }
}
In the case of the Entity model, the ClaimType property is a Flags Enum :-
[Flags]
public enum ClaimType : int
{
A = 1,
B = 2,
C = 4,
}
I want to be able to map from the Entity Model, to the Service Model. I need to map the ClaimType to List on the Service Model, but i have had no luck.
I'm new to AutoMapper, any help would be apreciated.

You need to create a property mapping for ClaimTypes, which converts each flags value to a string. There are a few ways to do this. Take a look at this answer for some ideas.
Here's how you can set it up in AutoMapper (using a quick and dirty method of just ToString()ing the enum then splitting the string):
Mapper.CreateMap<Lender, LenderServiceModel>()
.ForMember(dest => dest.ClaimTypes, opts => opts.MapFrom(src =>
src.ClaimTypes.ToString().Split(new string[]{", "}, StringSplitOptions.None).ToList()));
You can see a working .NETFiddle here

First you need to get hold of a string list representation of your enum flags, this can be done with this statement
var t = Enum.GetValues(typeof(ClaimType)).Cast<ClaimType>().Where(r => (claimType & r) == r).Select(r => r.ToString()).ToList();
For specific mappings with AutoMapper you need to specify it while setting it up.
So for this that will be following code: so we map ClaimTypes field from source to destination using the list conversion...
AutoMapper.Mapper.CreateMap<LegacyEntity, LenderServiceModel >()
.ForMember(dest => dest.ClaimTypes,
opts => opts.MapFrom(Enum.GetValues(typeof(ClaimType)).Cast<ClaimType>().Where(r => (src.ClaimTypes & r) == r).Select(r => r.ToString()).ToList());

Related

AutoMapper from list in DTO to individual object

I have the DTO below in which I need to map it to a flat view model, the idea is that some of the properties that come through from the request are shared, but there could be a list of names that come through.
public class ShinyDTO
{
public List<UserDetails> Users { get; set; }
public string SharedPropertyOne { get; set; }
public string SharedPropertyTwo { get; set; }
}
public class UserDetails
{
public string Title { get; set; }
public string Forename { get; set; }
public string Surname { get; set; }
}
public class MyRealClass
{
public string SharedPropertyOne {get;set;}
public string SharedPropertyTwo {get;set;}
public string Title {get;set;}
public string Forename {get;set;}
public string Surname {get;set;}
}
//This will map all the shared properties
MyRealClass request = Mapper.Map<MyRealClass>(dto);
foreach (var record in dto.Users){
//This bit overwrites the properties set above and then I only have the properties set for Forename, Surname, etc...
request = Mapper.Map<MyRealClass>(record);
}
I need to map this into a list of MyRealClass. I've tried creating seperate mappings and then looping it within a foreach, but this kept removing the initial attributes.
I've also tried setting up the second mapping to ignore the properties set above and I couldn't get this working, it was still overwriting the properties.
var autoMapperConfiguration = new MapperConfigurationExpression();
autoMapperConfiguration
.CreateMap<MyRealClass, UserDetails>()
.ForMember(c => c.SharedPropertyOne, d => d.Ignore())
.ForMember(c => c.SharedPropertyTwo, d => d.Ignore());
I think you're close, but your question states:
I need to map this into a list of MyRealClass
... and your attempted mapping maps MyRealClass to UserDetails. It seems like you actually want a map from UserDetails to MyRealClass instead.
Anyway, here's one way to accomplish this:
var autoMapperConfiguration = new MapperConfigurationExpression();
autoMapperConfiguration.CreateMap<UserDetails, MyRealClass>();
autoMapperConfiguration.CreateMap<ShinyDTO, MyRealClass>();
var results = new List<MyRealClass>();
foreach(var record in dto.Users) {
var mapped = Mapper.Map<MyRealClass>(dto);
Mapper.Map(record, mapped);
results.Add(mapped);
}
Here, the second Mapper.Map call maps record onto mapped, and it should not overwrite the values that have already been mapped over by the mapping from ShinyDTO to MyRealClass.
You could also get fancy and do all of this in a ConstructUsing call, but this seems clearer to me.
You can create a map between UserDetails and IEnumerable<MyRealClass>.
var autoMapperConfiguration = new MapperConfigurationExpression();
autoMapperConfiguration
.CreateMap<IEnumerable<MyRealClass>, UserDetails>()
.ForMember(dest => dest.SharedPropertyOne, opt => opt.MapFrom(x => x.get(0).SharedPropertyOne)); //you can check if the list is empty
.ForMember(dest => dest.SharedPropertyTwo, opt => opt.MapFrom(x => x.get(0).SharedPropertyTwo)); //you can check if the list is empty
.AfterMap((src,dest) => //src is a list type
{
foreach(MyRealClass myrealclass in src)
dest.Users.add(new UserDetails(){
Title = myrealclass.Title,
Forename = myrealclass.Forename,
Surname = myrealclass.Surname
});
});

Automapper Many to one map configuration

I want to map 3 different classes into a single DTO, each property have the same name on the source and the destination, the classes are the following:
User
Candidate
Portfolio
this is the DTO and how I want to map my objects:
public class CandidateTextInfo
{
public string ProfilePicture { get; set; } //-->User
public ObjectId UserId { get; set; } //-->User
public string Name { get; set; } //--> Candidate
public string Headline { get; set; } //--> Candidate
public Gender Gender { get; set; } //--> Candidate
public byte Rating { get; set; } //--> Candidate
public bool IsCompany { get; set; } //--> Candidate
public string[] Tags { get; set; } //--> Portafolio
public string[] Categories { get; set; } //--> Portafolio
public string ExecutiveSummary { get; set; } //--> Portafolio
public HourlyRate HourlyRate{ get; set; } //--> Candidate
}
I've been looking in SO and I found this solution but I don't get the method ConstructUsing
so how can I do to have a many to one mapping, is that possible, if not any workaround?
It depends greatly on the relationships between your objects. If you have a 1:1 relationship between your objects (e.g. if User has properties User.Candidate and User.Portfolio) then the mapping is easy:-
CreateMap<User, CandidateTextInfo>()
.ForMember(d => d.ProfilePicture, o => o.MapFrom(s => s.ProfilePicture)
// ...
.ForMember(d => d.Name, o => o.MapFrom(s => s.Candidate.Name)
// And so on...
If you don't have a one-to-one mapping, you need to arrange things a little bit yourself:-
public class CandidateTextInfoSource
{
public CandidateTextInfoSource(User user,
Candidate candidate,
Portafolio portafolio)
{
this.User = user;
this.Candidate = candidate;
this.Portafolio = portafolio;
}
public User User { get; set; }
public Candidate Candidate { get; set; }
public Portafolio Portafolio { get; set; }
}
// ...
CreateMap<CandidateTextInfoSource, CandidateTextInfo>()
.ForMember(d => d.ProfilePicture, o => o.MapFrom(s => s.User.ProfilePicture)
// ...
.ForMember(d => d.Name, o => o.MapFrom(s => s.Candidate.Name)
// And so on...
You can then use whatever means you require to create your CandidateTextInfoSource depending on the relationship between your objects. For example, if I assume that a User has a collection User.Candidates, and a Candidate has a property Candidate.Portfolio:-
CreateMap<User, IEnuemerable<CandidateTextInfoSource>>()
.ConstructUsing(
x => x.Candidates
.Select(y => Mapper.Map<CandidateTextInfo>(new CandidateTextInfoSource(x, y, y.Portfolio)))
.ToList());
I appreciate that this answer is very late, but if you further specify the relationship between your objects, I can help you create a more specific mapping.
Automapper's ConstructUsing is useful to build one property from custom code. In your case it is not really necessary. You just need to create the maps from your objects to your DTO. Then map each object instance to the same DTO instance.
However since Automapper wants each property of the destination object to be defined in order to ensure that the destination is fully specified you will need to configure each mapping with the properties not existing in the source object as ignored
CreateMap<Candidate, CandidateTextInfo>()
.ForMember(x=> x.ProfilePicture, opt => opt.Ignore())
.ForMember(...
// repeat for all destination properties not existing in source properties
If this is too much boilerplate code, many solutions are explored on stack overflow, among which this one looks promising: AutoMapper: "Ignore the rest"? (look at Robert Schroeder's answer)

How to use automapper with different properties names? [duplicate]

I am a newbie to the Automapper framework. I have a domain class and a DTO class as follows:
public class Employee
{
public long Id {get;set;}
public string Name {get;set;}
public string Phone {get;set;}
public string Fax {get;set;}
public DateTime DateOfBirth {get;set;}
}
public class EmployeeDto
{
public long Id {get;set;}
public string FullName {get;set;}
public DateTime DateOfBirth {get;set;}
}
Note: The name of property "Name" of Employee class is not the same as that of property "FullName" of EmployeeDto class.
And here's the code to map the Employee object to EmployeeDto:
Mapper.CreateMap<Employee, EmployeeDto>(); // code line (***)
EmployeeDto dto = Mapper.Map<Employee, EmployeeDto>(employee);
My question is: If I want to map Employee (source class) to EmployeeDto (destination class), how can I specify the mapping rule? In other words, how should I do more with code line (***) above?
Never mind, I myself found a solution:
Mapper.CreateMap<Employee, EmployeeDto>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.Name));
Just to roll the comments above into an updated approach using Automapper 8.1+...
var mapConfig = new MapperConfiguration(
cfg => cfg.CreateMap<Employee, EmployeeDto>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.Name))
);
Then you would build the mapper using the mapConfig:
var mapper = mapConfig.CreateMapper();
We can also specify on Class attributes for mapping
From https://docs.automapper.org/en/stable/Conventions.html#attribute-support
Attribute Support
AddMemberConfiguration().AddName<SourceToDestinationNameMapperAttributesMember>();
* Currently is always on
Looks for instances of SourceToDestinationMapperAttribute for
Properties/Fields and calls user defined isMatch function to find
member matches.
MapToAttribute is one of them which will match the property based on
name provided.
public class Foo
{
[MapTo("SourceOfBar")]
public int Bar { get; set; }
}
Considering that we have two classes
public class LookupDetailsBO
{
public int ID { get; set; }
public string Description { get; set; }
}
and the other class is
public class MaterialBO
{
[MapTo(nameof(LookupDetailsBO.ID))]
public int MaterialId { get; set; }
[MapTo(nameof(LookupDetailsBO.Description))]
public string MaterialName { get; set; }
public int LanguageId { get; set; }
}
In this way you know typically to which property you follow .
and you make sure of the naming convention , so if you have changed the propery name in the source . The MapTo() will prompt an error
The new style of Attribute Mapping via Data Annotations:
https://docs.automapper.org/en/v8.1.0/Attribute-mapping.html?highlight=annotation
[AutoMap(typeof(Order))]
public class OrderDto {
// This is equivalent to a CreateMap<Order, OrderDto>()
For mapping the member
[SourceMember(nameof(Order.OrderTotal))]
public decimal Total { get; set; }
If you want reverse map then you add that property in
[AutoMap(typeof(Order), ReverseMap = true )]
public class OrderDto {
// This is equivalent to a CreateMap<Order, OrderDto>().ReverseMap()
The above answers are great and hope OP has got his answer. I just want to add how we can map fixed values instead of fields using UseValue() method of IMemberConfigurationExpression Interface.
Mapper.CreateMap<Employee, EmployeeDto>()
.ForMember(dest => dest.Department, opt => opt.UseValue("Development"));
This will map "Development" as a Department property value for destination data.

NHIbernate SQLQuery and enum

I'm trying to map an enum field to a dto property. I'm using PositionalToBeanResultTransformer, something like this:
PositionalToBeanResultTransformer trans = new PositionalToBeanResultTransformer(typeof(DTOClass), new string[] { "Id", "EnumProperty" });
var data = Sesion.CreateSQLQuery("SELECT Id, EnumField FROM Table")
.AddScalar("EnumField", NHibernateUtil.Custom(typeof(Enumerador)))
.SetResultTransformer(trans)
.List<DTOClass>();
public class DTOClass
{
public int Id { get; set; }
public Enumerador EnumProperty { get; set; }
}
the DTOClass is not mapped so I can't use AddEntity() and the code with AddScalar() throws an error that I must implement NHibernate.UserTypes.IUserType.
How can I convert the string field of the DB to an enum in a SQLQuery?
Thanks in advance for the help.
If you are not using AutoMapping, in your ClassMap add a similar line like below:
Table("MyClass");
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Type).CustomType(typeof(Enumerations.MyType));
public virtual int Id { get; set; }
public virtual Enumerations.MyType Type { get; set; }
I usually store my enums as integers in the DB and the ClassMap will appropriately map back to the enumeration.

Need help with AutoMapper

I have two classes that looks as follows:
public class Rule
{
public int Id { get; set; }
public RuleGroup RuleGroup { get; set; }
}
public class RuleGroup
{
public int Id { get; set; }
public List<Rule> RuleList { get; set; }
}
A RuleGroup has a list of rules. My AutoMapper settings are as follows:
Mapper.CreateMap<RuleRecord, FirstSolar.Mes.Core.Entities.Recipe.Rule>()
.ForMember(destination => destination.RuleGroup, source => source.Ignore())
.ForMember(destination => destination.Id, source => source.MapFrom(item => item.RuleId));
Mapper.CreateMap<IList<RuleRecord>, IList<FirstSolar.Mes.Core.Entities.Recipe.Rule>>();
Mapper.CreateMap<RuleGroupRecord, FirstSolar.Mes.Core.Entities.Recipe.RuleGroup>()
.ForMember(destination => destination.Id, source => source.MapFrom(item => item.RuleGroupId));
Mapper.CreateMap<IList<RuleGroupRecord>, IList<FirstSolar.Mes.Core.Entities.Recipe.RuleGroup>>();
When I attempt to map a RuleGroupRecord (LinqToSQL object) to RuleGroup (DTO), AutoMapper says I need to add a mapping for RuleGroup.RuleList. I'm wondering why because I defined how to map a single RuleRecord and a List.
If I have to, how would I do it?
Simply add (I hope I got the syntax right, but you should see what I'm hinting at):
.ForMember(destination => destination.RuleList, source => source.MapFrom(item => item.Rules));
to the second mapping. While you handled the general mapping for RuleRecord to Rule in the first mapping, you didn't tell automapper to map the specific property RuleGroup.RuleList.

Categories

Resources