Automapper nested Collections without setter - c#

I have this code snippet running on LinqPad (C# program) with Automapper Nuget package 6.1.1 already included:
void Main()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Top, TopDto>().ReverseMap();
});
Mapper.AssertConfigurationIsValid();
var source = new TopDto
{
Id = 1,
Name = "Charlie",
Nicks = new List<string> { "Fernandez", "Others" }
};
var destination = Mapper.Map<Top>(source);
destination.Dump();
}
// Define other methods and classes here
public class Top
{
public Top()
{
Nicks = new List<string>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<string> Nicks { get; }
}
public class TopDto
{
public TopDto()
{
Nicks = new List<string>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<string> Nicks { get; set; }
}
And as you can see we have problems setting the nested Collection (without Setter at all). In theory this should be running fine but it is not adding any element to the Collection.
If we change the collection property adding a public setter, then all is fine.
How can I get a nested collection without adding a public setter or a setter at all?

Thanks to #LucianBargaoanu (in the comments) this is solved now, in this way:
void Main()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Top, TopDto>().ReverseMap()
.ForMember(d => d.Nicks, o=>
{
o.MapFrom(s => s.Nicks);
o.UseDestinationValue();
});
});
Mapper.AssertConfigurationIsValid();
var source = new TopDto(new List<string> { "Fernandez", "Others" })
{
Id = 1,
Name = "Charlie"
};
var destination = Mapper.Map<Top>(source);
destination.Dump();
}
// Define other methods and classes here
public class Top
{
public Top()
{
Nicks = new List<string>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<string> Nicks { get; }
}
public class TopDto
{
public TopDto(List<string> nicks)
{
Nicks = nicks;
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<string> Nicks { get; private set; }
}
Regards.

Related

Automapper override values which are not in source

Hi have a problem with auomapper where i try to use the Automapper.Mapper(src, dest) without loosing the values in the dest after doing the mapping.
I have a class which has a list of objects like below
public class UpdateShipmentDetailDto
{
public bool IsDocument { get; set; }
public List<UpdateItemDetailDto> ItemDetails { get; set; } = new();
}
which i want to map to
public class SCS_OUT_Manifest
{
public Guid ManifestId { get; set; }
public ICollection<SCS_OUT_ManifestItem> SCS_OUT_ManifestItems { get; set; } = new List<SCS_OUT_ManifestItem>();
}
The UpdateItemDetailDto class looks like this
public class UpdateItemDetailDto
{
public Guid ItemId { get; set; }
public string ItemDescription { get; set; }
public int Qty { get; set; }
public Guid UnitsId { get; set; }
public decimal ItemValue { get; set; }
}
And the SCS_OUT_ManifestItem class looke like
public class SCS_OUT_ManifestItem
{
public Guid ItemId { get; set; }
public Guid ManifestId { get; set; }
public string ItemDescription { get; set; }
public int Qty { get; set; }
public Guid UnitsId { get; set; }
public decimal ItemValue { get; set; }
}
Im performing a maaping like below, which map from ItemDetails (which is a list) to SCS_OUT_ManifestItems (which is also a ICollection).
_mapper.Map(updateShipmentDetailDto.ItemDetails, manifest.SCS_OUT_ManifestItems);
The problem after mapping is done the properties which in the destination collection are set to the default values.
for example the ManifestId inthe SCS_OUT_ManifestItem manifest.SCS_OUT_ManifestItems which is not in updateShipmentDetailDto.ItemDetails is set to its default Guid value 00000000-0000-0000-0000-000000000000.
But if i run this in a loop like below it works.
foreach (var item in manifest.SCS_OUT_ManifestItems)
{
_mapper.Map(updateShipmentDetailDto.ItemDetails.Single(s => s.ItemId == item.ItemId), item);
}
Please help! thanks in advance
Try map your lists and your itens and use the same name ÏtemDetails" in both lists.
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<UpdateItemDetailDto, SCS_OUT_ManifestItem>();
cfg.CreateMap<UpdateShipmentDetailDto, SCS_OUT_Manifest>();
});
var _mapper = config.CreateMapper();
var updateShipmentDetailDto = new UpdateShipmentDetailDto();
var updateItemDetailDto = new UpdateItemDetailDto();
var manifest = new SCS_OUT_Manifest();
updateItemDetailDto.ItemId = Guid.NewGuid();
updateItemDetailDto.UnitsId = Guid.NewGuid();
manifest.ManifestId = Guid.NewGuid();
updateItemDetailDto.ItemDescription = "test";
updateItemDetailDto.Qty = 10;
updateItemDetailDto.ItemValue = 25.50M;
updateShipmentDetailDto.ItemDetails = new List<UpdateItemDetailDto>();
updateShipmentDetailDto.ItemDetails.Add(updateItemDetailDto);
_mapper.Map(updateShipmentDetailDto.ItemDetails, manifest.ItemDetails);
Console.WriteLine($"DTO Guid: {updateShipmentDetailDto.ItemDetails[0].ItemId}, Desc: {updateShipmentDetailDto.ItemDetails[0].ItemDescription}");
foreach (var item in manifest.ItemDetails)
{
Console.WriteLine($"Guid: {item.ItemId}, Desc: {item.ItemDescription}");
}
Console.WriteLine($"Guid Manifest: {manifest.ManifestId}");

Linq query with complex model mapping

I am trying to map from my webapi to my solution some ICollection
this is a HttpClient: var currentNotifications = await client.GetUsersAsync();
If I have a model for example:
public class UserNotificationTypeDeliveryChoice
{
public DeliveryType DeliveryType { get; set; }
public NotificationGroup NotificationGroup{ get; set; }
}
public class DeliveryType
{
public byte DeliveryTypeId { get; set; }
public string Name { get; set; }
}
public class NotificationGroup
{
public byte NotificationGroupId { get; set; }
public string Name { get; set; }
}
and model to map to:
public class NotificationListViewModel
{
public DeliveryType DeliveryType { get; set; }
public NotificationGroup NotificationGroup { get; set; }
}
public class DeliveryType
{
public byte DeliveryTypeId { get; set; }
public string Name { get; set; }
}
public class NotificationGroup
{
public int NotificationGroupId { get; set; }
public string Name { get; set; }
}
So, I have an ICollection<UserNotificationTypeDeliveryChoice> currentNotifications from my webapi which I need to map to
List<NotificationListViewModel> notificationListViewModel
Myexample looks like:
var res = currentNotifications.ToList().ForEach(x => notificationListViewModel.Add(new NotificationListViewModel()
{
DeliveryType = new DeliveryType() {DeliveryTypeId = x.DeliveryType.DeliveryTypeId.Value, Name = x.DeliveryType.Name },
NotificationGroup = new NotificationGroup() { NotificationGroupId = x.NotificationGroup.NotificationGroupId.Value, Name = x.NotificationGroup.Name }
}));
But, it reports me an error: CS0815 Cannot assign void to an implicitly-typed variable
Ofc, if you have better solution, without ForEach I would be thankful.
ForEach() does not return anything.
You could do it with a Select() call instead:
var result = currentNotifications.Select(x => new NotificationListViewModel
{
DeliveryType = new DeliveryTypeViewModel
{
DeliveryTypeId = x.DeliveryType.DeliveryTypeId,
Name = x.DeliveryType.Name
},
NotificationGroup = new NotificationGroupViewModel
{
NotificationGroupId = x.NotificationGroup.NotificationGroupId,
Name = x.NotificationGroup.Name
}
});
I added ViewModel suffix to all view model class names to make it more readable.
The issue is here:
var res = currentNotifications.ToList().ForEach(...);
ForEach() returns void and therefore you're trying to assign void to res. That doesn't work.
First store the list, then do the ForEach:
var res = currentNotifications.ToList();
res.ForEach(...);
Or simply drop the res altogether, since you're storing the outcome in notificationListViewModel anyway.
currentNotifications.ToList().ForEach(...);
It depends on whether you still need res or not.
As an aside and further improvement, you're doing a LINQ Select() in a different way. Using the LINQ equivalent:
notificationListViewModel = currentNotifications.Select(x => new NotificationListViewModel() { ... });

Ignore Mapping of inner list property in AutoMapper --EF6,C#

I have a collection property of DTO like this
public ICollection<Applicant> Applicants{get;set;}
Applicant Model
public class Applicant
{
public int Id{get;set;}
public string name{get;set;}
public ICollection<ApplicantSkillsVM> ApplicantSkills { get; set; }
}
public class ApplicantSkillsVM
{
public int Id {get;set;}
public Skill skill{get;set;}
}
I want to map my List<iApplicant> DTO to entity given that I want to take ApplicantSkillsVM but ignore skill inside ApplicantSkillsVM.
I have a model which is list List<Applicant> and that contains another list List<ApplicantSkillsVM> and ApplicantSkillsVM has a property skill. I want to ignore this (skill) while mapping. Its simple.
How can I do this in latest the AutoMapper version with EF6?
Here a running sample:
internal class Program
{
#region Methods
private static void Main(string[] args)
{
// Configure the mappings
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ApplicantSkillVM, ApplicantSkill>().ForMember(x => x.Skill, x => x.Ignore()).ReverseMap();
cfg.CreateMap<ApplicantVM, Applicant>().ReverseMap();
});
var config = new MapperConfiguration(cfg => cfg.CreateMissingTypeMaps = true);
var mapper = config.CreateMapper();
ApplicantVM ap = new ApplicantVM
{
Name = "its me",
ApplicantSkills = new List<ApplicantSkillVM>
{
new ApplicantSkillVM {SomeInt = 10, SomeString = "test", Skill = new Skill {SomeInt = 20}},
new ApplicantSkillVM {SomeInt = 10, SomeString = "test"}
}
};
List<ApplicantVM> applicantVms = new List<ApplicantVM> {ap};
// Map
List<Applicant> apcants = Mapper.Map<List<ApplicantVM>, List<Applicant>>(applicantVms);
}
#endregion
}
/// Your source classes
public class Applicant
{
#region Properties
public List<ApplicantSkill> ApplicantSkills { get; set; }
public string Name { get; set; }
#endregion
}
public class ApplicantSkill
{
#region Properties
public Skill Skill { get; set; }
public int SomeInt { get; set; }
public string SomeString { get; set; }
#endregion
}
// Your VM classes
public class ApplicantVM
{
#region Properties
public List<ApplicantSkillVM> ApplicantSkills { get; set; }
public string Description { get; set; }
public string Name { get; set; }
#endregion
}
public class ApplicantSkillVM
{
#region Properties
public Skill Skill { get; set; }
public int SomeInt { get; set; }
public string SomeString { get; set; }
#endregion
}
public class Skill
{
#region Properties
public int SomeInt { get; set; }
#endregion
}
}
Initially my model ApplicantSkillsVM didnt have reference Id for Skill which should be nullable
So my model had to look like
public class ApplicantSkillsVM{
public int Id {get;set;}
public int? skillId{get;set;} //updated property
public Skill skill{get;set;}
}
The problem resolved

How to properly define AutoMapper config for complex class?

I'm working with Entity Framework, and getting ready to redo a project that is currently using EF->BDO->DTO->client with manual conversions for each class along the way.
I came across AutoMapper and think this would be a better solution overall. I have had no issues mapping CustomerEF -> CustomerDto, OfficeEF -> OfficeDto, etc, but I am now working on a more complex class, and struggling to get everything in place.
I feel I am close, and that something has to happen in reverse, but have not been able to identify what I'm missing.
public class CaseDto
{
public int CaseId { get; set; }
public string CaseReason { get; set; }
public CustomersDto _customer { get; set; }
public OfficeDto _office { get; set; }
public CaseNotesDto[] _caseNotes { get; set; }
}
public class CustomersDto
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
}
public class OfficeDto
{
public int OfficeId { get; set; }
public string OfficeName { get; set; }
}
public class CaseNotesDto
{
public int CaseNotesId { get; set; }
public int CaseId { get; set; }
public string CaseNote { get; set; }
}
// EF objects
public class CaseEF
{
public int CaseId { get; set; }
public string CaseReason { get; set; }
public int CustomerId { get; set; }
public int OfficeId { get; set; }
}
public class CustomerEF
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
}
public class OfficeEF
{
public int OfficeId { get; set; }
public string OfficeName { get; set; }
}
public class CaseNotesEF
{
public int CaseNotesId { get; set; }
public int CaseId { get; set; }
public string CaseNote { get; set; }
}
// execution classes
public class CaseFramework
{
// set 'ef' variables
private readonly OfficeEF _officeEf = new OfficeEF { OfficeId = 1, OfficeName = "Washington" };
private readonly CustomerEF _customerEf = new CustomerEF { CustomerId = 1, CustomerName = "Blips and Chitz" };
private readonly CaseNotesEF[] _caseNotesEf =
{
new CaseNotesEF {CaseNotesId = 1, CaseNote = "Case 1", CaseId = 1000},
new CaseNotesEF {CaseNotesId = 2, CaseNote = "Case 2", CaseId = 1000}
};
private readonly CaseEF _case =
new CaseEF { CaseId = 1000, CaseReason = "Roy is back!", CustomerId = 1, OfficeId = 1 };
public CaseDto GetCase(int caseId)
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<OfficeEF, OfficeDto>();
cfg.CreateMap<CustomerEF, CustomersDto>();
cfg.CreateMap<CaseNotesEF, CaseNotesDto>();
cfg.CreateMap<CaseEF, CaseDto>()
.ForMember(dest => dest._customer, opt => opt.MapFrom(src => src.CustomerId))
.ForMember(dest => dest._office, opt => opt.MapFrom(src => src.OfficeId))
.ForMember(dest => dest._caseNotes,
opt => opt.MapFrom(src => _caseNotesEf.Where(x => x.CaseId == caseId)));
});
Mapper.AssertConfigurationIsValid();
var source = new CaseEF { CaseId = caseId };
var destination = Mapper.Map<CaseEF, CaseDto>(source);
return destination;
}
}
To run this I am doing:
var b = new CaseFramework();
var result = b.GetCase(1000);
The results are populating the CaseId (set manually) and the CaseNotesDto, but nothing else.
Having the first 3 cfg.CreateMap items in the Mapper.Initialize section makes no difference if they are there or not.
Thanks in advance for any guidance.
Appreciate the responses.
#MickyD, I was a little confused on the POCO comment, as the EF already generated a tt, and the Canonical schema/model looks like something I'll have to research more.
#Steve I checked my navigation properties from my EF database and we need to do some fixes to the relationships before making the Include method work, but I think that will ultimately be the solution. In the meantime I was able to accomplish the original goal with this:
Mapper.AssertConfigurationIsValid();
var destination = Mapper.Map<CaseDto>(_case);
destination.Customers = Mapper.Map<CustomerEF, CustomersDto>(_customerEf);
destination.Office = Mapper.Map<OfficeEF, OfficeDto>(_officeEf);
return destination;
Thanks.

Convert to DTO list property

I have the following DTO:
public class QuestionGroupDTO : IBaseDTO
{
public string Description { get; set; }
public string Header { get; set; }
public Guid Id { get; set; }
public int Order { get; set; }
public IEnumerable<Services.Forms.Models.RelationForm_QuestionGroupDTO> RelationForms_QuestionGroups { get; set; }
public IEnumerable<RelationQuestionGroup_QuestionDTO> RelationQuestionGroups_Questions { get; set; }
}
I have problem with the RelationQuestionGroups_Questions while converting.
Here Is how my RelationQuestionGroup_QuestionDTO looks like
public class RelationQuestionGroup_QuestionDTO
{
public int Order { get; set; }
[Required]
public Guid QuestionGroupId { get; set; }
[Required]
public Guid QuestionId { get; set; }
public virtual QuestionGroupDTO QuestionGroup { get; set; }
public virtual QuestionDTO Question { get; set; }
}
Here Is how I convert:
public static QuestionGroupDTO ToDTO(this QuestionGroup src)
{
var dto = new QuestionGroupDTO
{
Id = src.Id,
Header = src.Header,
Description = src.Description,
RelationQuestionGroups_Questions = src.RelationQuestionGroups_Questions.ToList()
};
return dto;
}
As you can see, I'm trying to just assign It and make a list of It, but I got a casting error here. I'm not sure how to do this.
I get the following error:
Cannot implicity convert type Generic List to System.Collections.Generic.IEnumerble
You're having a great start at mapping, but at RelationQuestionGroups_Questions = src.RelationQuestionGroups_Questions.ToList(), you're trying to assign a List<Entity> to List<Dto>. You can't do that.
You need to map any non-primitive properties as well. You can do that like this:
public static QuestionGroupDTO ToDTO(this QuestionGroup src)
{
var dto = new QuestionGroupDTO
{
// ...
RelationQuestionGroups_Questions = src.RelationQuestionGroups_Questions
.Select(ToDTO)
.ToList()
};
return dto;
}
Then you add a method to map RelationQuestionGroups_Question to RelationQuestionGroups_QuestionDTO:
public RelationQuestionGroups_QuestionDTO ToDTO(RelationQuestionGroups_Question entity)
{
return new RelationQuestionGroups_QuestionDTO
{
Order = entity.Order,
// ...
};
}
And then you'll go look at AutoMapper to automate this.
You forgot to map RelationQuestionGroups_Questions to RelationQuestionGroup_QuestionDTO.
public static QuestionGroupDTO ToDTO(this QuestionGroup src)
{
var dto = new QuestionGroupDTO
{
Id = src.Id,
Header = src.Header,
Description = src.Description,
RelationQuestionGroups_Questions = src.RelationQuestionGroups_Questions.Select(rq => new RelationQuestionGroup_QuestionDTO
{
Order = rq.Order,
QuestionGroupId = rq.QuestionGroupId,
QuestionId = rq.QuestionId
}).ToList()
};
return dto;
}

Categories

Resources