Why does my Automapper config not work, even though AssertConfigurationValid passes? - c#

I have the following entities:
public class ContactDetailsJson
{
public string Zip { get; set; }
public string Phone { get; set; }
public string AreaCode { get; set; }
public DateTime UpdatedDate { get; set; }
public string SeoContactUrl { get; set; }
public string State { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string CompanyName { get; set; }
public string ContactUrl { get; set; }
public string Country { get; set; }
public bool Owned { get; set; }
public string City { get; set; }
public string Title { get; set; }
public string Email { get; set; }
public string Address { get; set; }
public string CompanyId { get; set; }
public string ContactId { get; set; }
}
public class ExternalContactSearchResultsViewModel
{
public int DisplayedPageNumber { get; set; }
public int TotalResultsCount { get; set; }
public int PageSize { get; set; }
public IList<ContactResultViewModel> Results { get; set; }
public class ContactResultViewModel
{
public string ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Headline { get; set; }
public string Company { get; set; }
public string PublicUrl { get; set; }
public bool HasAccess { get; set; }
public DateTime LastUpdatedDate { get; set; }
}
}
To support the conversion, I have the following mappings created, which I have verified are being run:
Mapper.CreateMap<ContactDetailsJson, ExternalContactSearchResultsViewModel.ContactResultViewModel>()
.ForMember(dest => dest.Company, opt => opt.MapFrom(src => src.CompanyName))
.ForMember(dest => dest.ContactId, opt => opt.MapFrom(src => src.ContactId))
.ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.FirstName))
.ForMember(dest => dest.LastName, opt => opt.MapFrom(src => src.LastName))
.ForMember(dest => dest.HasAccess, opt => opt.MapFrom(src => src.Owned))
.ForMember(dest => dest.Headline, opt => opt.MapFrom(src => src.Title))
.ForMember(dest => dest.LastUpdatedDate, opt => opt.MapFrom(src => src.UpdatedDate))
.ForMember(dest => dest.PublicUrl, opt => opt.Ignore());
Unfortunately, this mapping results in all properties being null (or default values), as can be seen from the following unit test
[TestMethod]
public void Automapper_Contact_Details_Json_Can_Be_Mapped()
{
// Setup
EntityMapLoader.LoadEntityMappings();
DateTime testDate = DateTime.Now;
var json = new ContactDetailsJson
{
CompanyName = "company",
ContactId = "12345",
FirstName = "first",
LastName = "last",
Owned = true,
Title = "title",
UpdatedDate = testDate
};
// Act
var result = Mapper.Map<ContactDetailsJson, ExternalContactSearchResultsViewModel.ContactResultViewModel>(json);
// Verify
Assert.IsNotNull(result, "result was null");
Assert.AreEqual("company", result.Company);
Assert.AreEqual("12345", result.ContactId);
Assert.AreEqual("first", result.FirstName);
Assert.AreEqual("last", result.LastName);
Assert.AreEqual(true, result.HasAccess);
Assert.AreEqual("title", result.Headline);
Assert.AreEqual(testDate, result.LastUpdatedDate);
}
I can't figure out what is wrong. Does anyone else see anything?

I tried this and it works for me, I think its something wrong in way you are loading mapping or check if you are using latest version of automapper
[TestFixture]
public class UnitTest
{
[Test]
public void Automapper_Contact_Details_Json_Can_Be_Mapped()
{
// Setup
DateTime testDate = DateTime.Now;
var json = new ContactDetailsJson
{
CompanyName = "company",
ContactId = "12345",
FirstName = "first",
LastName = "last",
Owned = true,
Title = "title",
UpdatedDate = testDate
};
Mapper.CreateMap<ContactDetailsJson, ExternalContactSearchResultsViewModel.ContactResultViewModel>()
.ForMember(dest => dest.Company, opt => opt.MapFrom(src => src.CompanyName))
.ForMember(dest => dest.ContactId, opt => opt.MapFrom(src => src.ContactId))
.ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.FirstName))
.ForMember(dest => dest.LastName, opt => opt.MapFrom(src => src.LastName))
.ForMember(dest => dest.HasAccess, opt => opt.MapFrom(src => src.Owned))
.ForMember(dest => dest.Headline, opt => opt.MapFrom(src => src.Title))
.ForMember(dest => dest.LastUpdatedDate, opt => opt.MapFrom(src => src.UpdatedDate))
.ForMember(dest => dest.PublicUrl, opt => opt.Ignore());
// Act
var result = Mapper.Map<ContactDetailsJson, ExternalContactSearchResultsViewModel.ContactResultViewModel>(json);
// Verify
Assert.IsNotNull(result, "result was null");
Assert.AreEqual("company", result.Company);
Assert.AreEqual("12345", result.ContactId);
Assert.AreEqual("first", result.FirstName);
Assert.AreEqual("last", result.LastName);
Assert.AreEqual(true, result.HasAccess);
Assert.AreEqual("title", result.Headline);
Assert.AreEqual(testDate, result.LastUpdatedDate);
}
}

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));

Automapper , map list of long to an object nested

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()));

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