Automapper , map list of long to an object nested - c#

I have four classes
public class Status
{
public long Key { get; set; }
public string DisplayString { get; set; }
}
public class Attachment
{
public long Key { get; set; }
public string FileName { get; set; }
public long FileSize { get; set; }
public string ExternalKey_SO { get; set; }
}
public class TaskClick
{
public long Key { get; set; }
public string CallId { get; set; }
public Status Status { get; set; }
public List<Attachment> Attachments { get; set; }
}
public class TaskClickDto
{
public long Key { get; set; }
public string CallId { get; set; }
public string Status { get; set; }
public List<long> AttachmentKeys { get; set; }
}
I donĀ“t know how to map a list of TaskClickDto.AttachmentKeys to TaskClick.Attachments
AttachmentKeys is a list of all the Keys of Taskclick
My automapper configuration
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<TaskClick, TaskClickDto>()
//TaskClick --> TaskClickDto works ok
.ForMember(dest => dest.CallId, opt => opt.MapFrom(src => src.CallId))
.ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status.DisplayString))
.ForMember(dest => dest.AttachmentKeys, opt => opt.MapFrom(src => src.Attachments.Select(k => k.Key)))
.ReverseMap()
//TaskClickDto --> TaskClick works Ko
.ForPath(dest => dest.Status.DisplayString, opt => opt.MapFrom(src => src.Status))
.ForPath(dest => dest.Attachments, ????));
}
}
So I need to know how to create a new list of Attachment , for each one, map the key and ignore the rest of the properties.
Best regards.
jolynice

This should do it:
.ForPath(dest => dest.Attachments, opt =>
opt.MapFrom(src => src.AttachmentKeys.Select(k =>
new Attachment { Key = k }).ToList()));

Related

Missing Typemap configuration automapper error when map into model class

I have 2 classses like this
public class ListOfBMTTeamMapping
{
public class TeamMapping
{
public List<TeamMappings> results { get; set; }
}
public class TeamMappings
{
public int id { get; set; }
public string areaPath { get; set; }
public string agileReleaseTrainName { get; set; }
public string deliveryTeamName { get; set; }
public string keyedInTeamCode { get; set; }
public string deliveryTeamId { get; set; }
public bool isDeleted { get; set; }
public string modified { get; set; }
public string modifiedBy { get; set; }
}
}
and next one
public class BmtAdoMapping
{
public int? Id { get; set; }
public string AreaPath { get; set; }
public string AgileReleaseTrainName { get; set; }
public string DeliveryTeamName { get; set; }
public string KeyedInTeamCode { get; set; }
public string DeliveryTeamId { get; set; }
public string IsDeleted { get; set; }
public DateTime? Modified { get; set; }
public string ModifiedBy { get; set; }
}
Here is the first class is defined to deserialize a third party API data like this
{
"results": [
{
"id": 6534,
"areaPath": "rtyty",
"agileReleaseTrainName": "rtyrty",
"deliveryTeamName": "fggfhfg",
"keyedInTeamCode": "vbnvbn",
"deliveryTeamId": 3254,
"isDeleted": false,
"modified": "2019-03-27T09:31:50.4581912",
"modifiedBy": "balassm"
},
{
"id": 6536,
"areaPath": "werwbvcbvcb",
"agileReleaseTrainName": "vbvbv",
"deliveryTeamName": "Aerere",
"keyedInTeamCode": "GTer33",
"deliveryTeamId": 3256,
"isDeleted": false,
"modified": "2019-03-27T09:31:50.4792922",
"modifiedBy": "balassm"
}
]
}
So here is my code
ListOfBMTTeamMapping.TeamMapping Results = new ListOfBMTTeamMapping.TeamMapping();
Results = JsonConvert.DeserializeObject<ListOfBMTTeamMapping.TeamMapping>(responseBody);
List<BmtAdoMapping> bmt = new List<BmtAdoMapping>();
bmt = _mapper.Map<List<BmtAdoMapping>>(Results);
Everythnig working fine and Results variable getting deserialized data. But my second class which is actually
a model class for my entity framework and on trying to map into that model class, I am getting an error like this
**AutoMapper.AutoMapperMappingException:** 'Missing type map configuration or unsupported mapping.'
Mapping types:
Object -> List`1
System.Object -> System.Collections.Generic.List`1
[[EPMO_Toolset_API.common.BmtAdoMapping, EPMO-Toolset-API, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
Here is my Automapper profile
CreateMap<ListOfBMTTeamMapping, JsonBmtAdoMapping>();
CreateMap<ListOfBMTTeamMapping.TeamMappings, JsonBmtAdoMapping>()
.ForMember(dest => dest.AgileReleaseTrainName, opt => opt.MapFrom(src => src.agileReleaseTrainName))
.ForMember(dest => dest.AreaPath, opt => opt.MapFrom(src => src.areaPath))
.ForMember(dest => dest.DeliveryTeamId, opt => opt.MapFrom(src => src.deliveryTeamId))
.ForMember(dest => dest.DeliveryTeamName, opt => opt.MapFrom(src => src.deliveryTeamName))
.ForMember(dest => dest.IsDeleted, opt => opt.MapFrom(src => src.isDeleted))
.ForMember(dest => dest.KeyedInTeamCode, opt => opt.MapFrom(src => src.keyedInTeamCode))
.ForMember(dest => dest.Modified, opt => opt.MapFrom(src => src.modified))
.ForMember(dest => dest.ModifiedBy, opt => opt.MapFrom(src => src.modifiedBy));

How to Map (using AutoMapper) entities that have a ForeignKey in ASP.NET CORE 3.1.1 (C#,EntityFrameworkCore)

I have this function in my controller that creates a an entity:
[HttpPost]
[ProducesResponseType(typeof(ConnectionDBResponse), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<ConnectionDBResponse>> PostConnectionDB([FromBody] CreateConnectionDBQuery query)
{
var connectionDBs = _mapper.Map<ConnectionDBDataModel>(query);
_context.ConnectionDB.Add(connectionDBs);
await _context.SaveChangesAsync();
var connectionDBResponse = _mapper.Map<ConnectionDBResponse>(connectionDBs);
return CreatedAtAction(nameof(GetAllConnectionDB), new { id = connectionDBs.Id }, connectionDBResponse);
}
For that I'm mapping between these two classes:
The response Class:
public class CreateConnectionDBQuery
{
public string ServerType { get; set; }
public string ServerName { get; set; }
public string port { get; set; }
public string AuthType { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string DBName { get; set; }
public string FolderName { get; set; }
public ScheduleConfigResponse ScheduleConfig { get; set; }
public Boolean hasEmails { get; set; }
public EmailConfigResponse EmailConfig { get; set; }
}
public class CreateScheduleConfigQuery
{
public string HourOfSave { get; set; }
public int NumDaysInDB { get; set; }
public CreateConnectionDBQuery ConnDB { get; set; }
public int ConnDBForeignKey { get; set; }
}
public class CreateEmailConfigQuery
{
public string SuccesEmail { get; set; }
public string FailureEmail { get; set; }
public CreateConnectionDBQuery ConnDB { get; set; }
public int ConnDBForeignKey { get; set; }
}
And the dataModel Class:
[Table("ConnectionDB")]
public class ConnectionDBDataModel
{
[Key]
public int Id { get; set; }
[Required]
public string ServerType { get; set; }
[Required]
public string ServerName { get; set; }
public string port { get; set; }
public string AuthType { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
[Required]
public string DBName { get; set; }
[Required]
public string FolderName { get; set; }
public ScheduleConfigDataModel ScheduleConfig { get; set; }
public Boolean hasEmails { get; set; }
public EmailConfigDataModel EmailConfig { get; set; }
}
[Table("ScheduleConfig")]
public class ScheduleConfigDataModel
{
[Key]
public int Id { get; set; }
public string HourOfSave { get; set; }
public int NumDaysInDB { get; set; }
public int ConnDBForeignKey { get; set; }
public ConnectionDBDataModel ConnDB { get; set; }
}
[Table("EmailConfig")]
public class EmailConfigDataModel
{
[Key]
public int Id { get; set; }
public string SuccesEmail { get; set; }
public string FailureEmail { get; set; }
public int ConnDBForeignKey { get; set; }
public ConnectionDBDataModel ConnDB { get; set; }
}
For that I'm using the AutoMapper as following:
#region ConnectionDB
CreateMap<ConnectionDBDataModel, ConnectionDBResponse>();
CreateMap<CreateConnectionDBQuery, ConnectionDBDataModel>()
.ForMember(dest => dest.Id, opt => opt.Ignore());
CreateMap<UpdateConnectionDBQuery, ConnectionDBDataModel>()
.ForMember(dest => dest.Id, opt => opt.Ignore());
#endregion
#region ScheduleConfig
CreateMap<ScheduleConfigDataModel, ScheduleConfigResponse>()
.ForMember(dest => dest.ConnDBForeignKey, opt => opt.Ignore());
CreateMap<CreateScheduleConfigQuery, ScheduleConfigDataModel>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.ConnDBForeignKey, opt => opt.Ignore());
CreateMap<UpdateScheduleConfigQuery, ScheduleConfigDataModel>()
.ForMember(dest => dest.Id, opt => opt.Ignore());
#endregion ScheduleConfig
#region EmailConfig
CreateMap<EmailConfigDataModel, EmailConfigResponse>()
.ForMember(dest => dest.ConnDBForeignKey, opt => opt.Ignore());
CreateMap<CreateEmailConfigQuery, EmailConfigDataModel>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.ConnDBForeignKey, opt => opt.Ignore());
CreateMap<UpdateEmailConfigQuery, EmailConfigDataModel>()
.ForMember(dest => dest.Id, opt => opt.Ignore());
#endregion
But when I try to create this element it gives me an error saying that it's coming from an invalid Mapping as shown in the screen bellow:
I have tried to Ignore the Foreign Key (because my guess that this problem is coming from the foreignKey) using this line of code : .ForMember(dest => dest.ConnDBForeignKey, opt => opt.Ignore()); , but I guess it's not the way to solve that problem.
Any help would be appreciated thank you!
The error is happening because when mapping CreateConnectionDBQuery to ConnectionDBDataModel, there is no mapping defined for the types of the ScheduleConfig properties.
I'm guessing that in your CreateConnectionDBQuery, your ScheduleConfig property should be of type CreateScheduleConfigQuery instead of ScheduleConfigResponse.
Alternatively, if you don't want to change the models, you could add a mapping configuration from ScheduleConfigResponse to ScheduleConfigDataModel. But that doesn't seem very intuitive.

Mapping list in automapper

I am new to automapper. I have a class that has a collection of object. And I need that class to map to another object that has a different name. I need to map the employee to employeeDto. Both classes has different name and different propery name. Can they be mapped using automapper if they have a different name?
public class Employee
{
public string Name { get; set; }
public string Address { get; set; }
public List<Job> Jobs { get; set; }
}
public class Job
{
public string CompanyName { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
public class EmployeeDto
{
public string Fullname { get; set; }
public string Location { get; set; }
public List<WorkExperience> WorkExperience { get; set; }
}
public class WorkExperience
{
public string NameOfCompany { get; set; }
public DateTime DateBegin { get; set; }
public DateTime DateEnd { get; set; }
}
You can configure mapping to handle you Jobs list to WorkExperience list manually. See Projection section in AutoMapper documentation
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<Employee, EmployeeDto>()
.ForMember(dest => dest.Fullname, opt => opt.MapFrom(src => src.Name))
.ForMember(dest => dest.Location, opt => opt.MapFrom(src => src.Address))
.ForMember(dest => dest.WorkExperience, opt => opt.MapFrom(src => src.Jobs));
cfg.CreateMap<Job, WorkExperience>()
.ForMember(dest => dest.NameOfCompany, opt => opt.MapFrom(src => src.CompanyName))
.ForMember(dest => dest.DateBegin, opt => opt.MapFrom(src => src.StartDate))
.ForMember(dest => dest.DateEnd, opt => opt.MapFrom(src => src.EndDate));
});
Additionally, if you want to convert in the opposite direction, you can add ReverseMap() to avoid code duplication.

Add object when mapping in Automapper

I am trying to map from an Entity Framework object to the DTO object, I am getting the error on the automapper profile:
The type 'ClaimToTemplateClaim' cannot be used as typed parameter
'TValueResolver' in the generic type or method
'IMemberConfigurationExpression<TemplateEntity, Template,object>.ResolveUsing<TValueResolver>()'.
There is no implicit reference conversion from 'ClaimToTemplateClaim'
to 'AutoMapper.IValueResolver<GPS.Auth2.Service.DataAccess.Objects.TemplateEntity, GPSAuth2ServiceInterface.Objects.Template, object>.'
Source Object DTO
public class Template
{
public Template()
{
this.Claims = new List<Claim>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<Claim> Claims { get; set; }
}
public class Claim
{
public int Id { get; set; }
public int ProductNumber { get; set; }
public string TheClaim { get; set; }
}
Entity Object (adds a join table)
public class TemplateEntity
{
public TemplateEntity()
{
this.TemplateClaims = new List<TemplateClaimEntity>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<TemplateClaimEntity> TemplateClaims { get; set; }
}
public class TemplateClaimEntity
{
public int TemplateId { get; set; }
public int ClaimId { get; set; }
public ClaimEntity Claim { get; set; }
public TemplateEntity Template { get; set; }
}
public class TemplateEntity
{
public TemplateEntity()
{
this.TemplateClaims = new List<TemplateClaimEntity>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
When someone passes my a claim, I need it to essentially map to a ClaimEntity object, but also create a TemplateClaimObject.
AutoMapper Profile
public Auth2SetupMappings()
{
this.CreateMap<Template, TemplateEntity>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description))
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name))
.ForMember(dest => dest.TemplateClaims, opt => opt.ResolveUsing<TemplateClaimToClaim>()) <- ERROR HERE
.ReverseMap()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description))
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name))
.ForMember(dest => dest.Claims, opt => opt.ResolveUsing<ClaimToTemplateClaim>()); <- ERROR HERE
}
public class ClaimToTemplateClaim : IValueResolver<Claim, TemplateClaimEntity, TemplateClaimEntity>
{
public TemplateClaimEntity Resolve(Claim source, TemplateClaimEntity destination, TemplateClaimEntity member, ResolutionContext context)
{
return new TemplateClaimEntity
{
Claim = new ClaimEntity
{
Claim = source.TheClaim,
Id = source.Id,
ProductNumber = source.ProductNumber
}
};
}
}
public class TemplateClaimToClaim : IValueResolver<TemplateClaim, Claim, Claim>
{
public Claim Resolve(TemplateClaim source, Claim destination, Claim member, ResolutionContext context)
{
return new Claim
{
ProductNumber = source.Claim.ProductNumber,
Id = source.Claim.Id,
TheClaim = source.Claim.TheClaim
};
}
}
Essentially we do not want to pass the join table to the DTO.

AutoMapper map complex object

I've been trying to use AutoMapper, but I'am having trouble configuring the map.
Having this:
public class A
{
public B b { get; set; }
public C c { get; set; }
public int id { get; set; }
public string X { get; set; }
}
public class B
{
public int id { get; set; }
public string Y { get; set; }
}
public class C
{
public int id { get; set; }
public string Z { get; set; }
}
public class ABC
{
public int Aid { get; set; }
public string AX { get; set; }
public int Bid { get; set; }
public string BY { get; set; }
public int Cid { get; set; }
public string CZ { get; set; }
}
How can I do mapping A > ABC and ABC > A.
I don't want to map each property manually. Is it possible?
Thank you.
I am not sure what you mean by "I don't want to map each property manually". But with AutoMapper you could easily map the properties:
Mapper.Initialize(cfg => cfg.CreateMap<A, ABC>()
.ForMember(dest => dest.AX, opt => opt.MapFrom(src => src.X))
.ForMember(dest => dest.Aid, opt => opt.MapFrom(src => src.id))
.ForMember(dest => dest.BY, opt => opt.MapFrom(src => src.b.Y))
.ForMember(dest => dest.Bid, opt => opt.MapFrom(src => src.b.id))
.ForMember(dest => dest.CZ, opt => opt.MapFrom(src => src.c.Z))
.ForMember(dest => dest.Cid, opt => opt.MapFrom(src => src.c.id)));
var a = new A
{
X = "I am A",
id = 0,
b = new B()
{
Y = "I am B",
id = 1
},
c = new C()
{
Z = "I am C",
id = 2
}
};
var abc = Mapper.Map<A, ABC>(a);

Categories