AutoMapper to map a nested list object - c#

I have 2 objects : Parent and ParentDTO :
class ParentDTO
{
public string AttA { get; set; }
public List<List<BDto>> AttrBNestedList { get; set; }
}
class BDto
{
public string AttrC { get; set; }
public int AtterD { get; set; }
public string AtterE { get; set; }
public CDto AttrFobj { get; set; }
}
class CDto
{
public string AttrG { get; set; }
public int AtterH { get; set; }
}
//--------------------------------------
class Parent
{
public string AttA { get; set; }
public List<B> AttrBList { get; set; }
}
class B
{
public string AttrC { get; set; }
public int AtterD { get; set; }
public string AtterE { get; set; }
public C AttrFobj { get; set; }
public C AttrGobj { get; set; }
public C AttrHobj { get; set; }
}
class C
{
public string AttrI { get; set; }
public string AttrJ { get; set; }
public int AtterK { get; set; }
}
I want to using AutoMapper to map data from ParentDto object to Parent object
(all data in ParentDto has to transfer to Parent, but first element from AttrBNestedList copy to AttrBList)
Mapper.CreateMap<CDto, C>();
Mapper.CreateMap<BDto, B>();
Mapper.CreateMap<ParentDto, Parent>()
.ForMember(dest => dest.AttrBList , opt => opt.AttrBNestedList.singleorDefault??);
I need just singleorDefault item from List<List<BDto>> AttrBNestedList map to List<B> AttrBList
Can anyone help me. Thanks

There is a syntax error which doesn't allow you to use SingleOrDefault(), you need to use MapFrom method:
Mapper.CreateMap<ParentDTO, Parent>().ForMember(dest => dest.AttrBList, opt => opt.MapFrom(src => src.AttrBNestedList.SingleOrDefault()));

Related

Automapper Finding Not Mapped properties

Using Automapper for a project, just mapping 2 objects to each other, nothing fancy. I must have something configured incorrectly because AutoMapper keeps saying that there are UnMapped Properties.
Here's the AutoMapper config.
var mapperConfig = new MapperConfiguration(cfg => {cfg.CreateMap<SrcObj, DestObj>()
.ForMember(dest => dest.Level, opt => opt.MapFrom(src => src.lvl));}
mapperConfig.AssertConfigurationIsValid();
SrcObj
public class SrcObj
{
public int Id { get; set; }
public int ParentNode { get; set; }
public string Controller { get; set; }
public string Action { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public bool? IsActive { get; set; }
public string AreaName { get; set; }
public int? DisplayOrder { get; set; }
public Int64 Type{ get; set; }
public int lvl { get; set; }
}
DestObj
public class DestObj
{
public int Id { get; set; }
public int ParentNode { get; set; }
public string Controller { get; set; }
public string Action { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public bool? IsActive { get; set; }
public string AreaName { get; set; }
public int? DisplayOrder { get; set; }
public Int64 Type{ get; set; }
public int Level { get; set; }
}
And the Implementation:
var items = await _context.Database.SqlQuery<SrcObj>($"EXEC spGenerateMenu {app1}").ToListAsync();
var rslt = _mapper.Map<DestObj>(items);
and the error:
{"\nUnmapped members were found. Review the types and members below.\nAdd a custom mapping expressio...}
The error actually lists every member of the DestObj. Not sure what I'm missing. probably something simple
Because your source is a List, you need to map it also to a List:
var rslt = _mapper.Map<List<DestObj>>(items);

AutoMapper Map derived classes to property of base type collection

I'm trying to map two different objects to objects that are derived from an interface. Additionally, I need to have another property mapped to the derived types from the dtos. Given this object structure:
public interface ICoverage
{
string Name { get; set; }
string Code { get; set; }
}
public class CoverageA : ICoverage
{
public string Name { get; set; }
public string Code { get; set; }
public string Current { get; set; }
}
public class CoverageB : ICoverage
{
public string Name { get; set; }
public string Code { get; set; }
public bool HasRecord { get; set; }
}
public class Application
{
public int ApplicationId { get; set; }
public string Code { get; set; }
public List<ICoverage> Coverages { get; set; }
public Application()
{
Coverages = new List<ICoverage>();
}
}
public class StagingDto
{
public string Referrer { get; set; }
public string Code { get; set; }
public CoverageADto CoverageA { get; set; }
public CoverageBDto CoverageB { get; set; }
}
public class CoverageADto
{
public string Current { get; set; }
}
public class CoverageBDto
{
public bool HasRecord { get; set; }
}
This mapping below works but I am wondering if there is a better way to do it:
cfg.CreateMap<StagingDto, Application>()
.AfterMap((src, dest) => dest.Coverages.Add(new CoverageB()
{
HasRecord = src.CoverageB.HasRecord,
Code = src.Code
}))
.AfterMap((src, dest) => dest.Coverages.Add(new CoverageA()
{
Current = src.CoverageA.Current,
Code = src.Code
}));
Ideally I'd like to stay away from having to create any extension method.
For me it looks a bit better:
cfg.CreateMap<StagingDto, Application>()
.ForMember(dest => dest.Coverages,
opt => opt.ResolveUsing(src => new ICoverage[]
{
new CoverageA
{
Current = src.CoverageA.Current,
Code = src.Code
},
new CoverageB
{
HasRecord = src.CoverageB.HasRecord,
Code = src.Code
}
}));

Automapper many to many mapping confusion

I have many to many relationship tables such as "User & Notification & UserNotification" and their entities, view models also.
There is only a difference between ViewModel and Entity classes. HasRead property is inside NotificationViewModel. How Can I map this entities to view models? I could not achieve this for HasRead property.
What I did so far is,
Mapping Configuration:
CreateMap<Notification, NotificationViewModel>();
CreateMap<User, UserViewModel>().ForMember(dest => dest.Notifications, map => map.MapFrom(src => src.UserNotification.Select(x => x.Notification)));
Notification class:
public class Notification : IEntityBase
{
public Notification()
{
this.UserNotification = new HashSet<UserNotification>();
}
public int Id { get; set; }
public string Header { get; set; }
public string Content { get; set; }
public System.DateTime CreateTime { get; set; }
public bool Status { get; set; }
public virtual ICollection<UserNotification> UserNotification { get; set; }
}
User Class
public class User : IEntityBase
{
public User()
{
this.UserNotification = new HashSet<UserNotification>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public bool Status { get; set; }
public virtual ICollection<UserNotification> UserNotification { get; set; }
}
UserNotification class:
public class UserNotification : IEntityBase
{
public int Id { get; set; }
public int UserId { get; set; }
public int NotificationId { get; set; }
public bool HasRead { get; set; }
public virtual Notification Notification { get; set; }
public virtual User User { get; set; }
}
UserViewModel class
public class UserViewModel : IValidatableObject
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public bool Status { get; set; }
public IList<NotificationViewModel> Notifications { get; set; }
}
NotificationViewModel class
public class NotificationViewModel
{
public int Id { get; set; }
public string Header { get; set; }
public string Content { get; set; }
public System.DateTime CreateTime { get; set; }
public bool Status { get; set; }
public bool HasRead { get; set; } // this is the difference
}
In order to fix up the HasRead, maybe you can utilize the AfterMap(Action<TSource, TDestination> afterFunction) function. It's not as elegant as the rest of automapper, but it might work.
CreateMap<User, UserViewModel>()
.ForMember(dest => dest.Notifications, map => map.MapFrom(src => src.UserNotification.Select(x => x.Notification)))
.AfterMap((src, dest) =>
{
foreach (var notificationVM in dest.Notifications)
{
notificationVM.HasRead = src.UserNotification.Where(x => x.NotificationId == notificationVM.Id).Select(x => x.HasRead).FirstOrDefault();
}
});

c# SelectMany, gather information from a nested model in a list

I am working on a project and I am trying to clean up some code. I can get the information I need using 2 lines, however I am new to using SelectMany and figure maybe I am looking at this wrong. So what I'd like to to take my two lines of code and turn it into 1 line of code if at all possible.
Selecting: I need the BambooDeployModel by a particular name, in the example 'test' is the variable for this.
Then once it narrows down my list to a particular model, I need to select the "Test" environment by name.
Here are my lines as is:
List<BambooDeployModel> deployments
var q = deployments.SelectMany(b => b.environments.Where(f => f.name == "Test"), (b, f) => f).SelectMany(f => deployments.Where(o => o.name == test)).Distinct();
var p = q.SelectMany(b => b.environments).Where(f => f.name == "Test").Distinct();
And here is the model, it is just a generic model based on values received from Bamboo based on deployments in our system.
public class BambooDeployModel
{
public int id { get; set; }
public GenericSetKey key { get; set; }
public string name { get; set; }
public GenericSetKey planKey { get; set; }
public string description { get; set; }
public List<Environment> environments { get; set; }
public Operations operations { get; set; }
}
public class GenericSetKey
{
public KeyValuePair<string, string> key { get; set; }
}
public class Environment
{
public int id { get; set; }
public GenericSetKey key { get; set; }
public string name { get; set; }
public string description { get; set; }
public int deploymentProjectId { get; set; }
public Operations operations { get; set; }
public string position { get; set; }
public string configurationState { get; set; }
}
public class Operations
{
public bool canView { get; set; }
public bool canEdit { get; set; }
public bool canDelete { get; set; }
public bool allowedToExecute { get; set; }
public bool canExecute { get; set; }
public bool allowedToCreateVersion { get; set; }
public bool allowedToSetVersionStatus { get; set; }
}

AutoMapper "Member not found" with UseDestinationValue

I am trying to use AutoMapper to map a ViewModel to a Model.
Here is my simplified ViewModel (the source) class:
public class EditPaypointVM
{
public Int64 Id { get; set; }
public Int64 OrganisationId { get; set; }
[Required]
public string OrganisationContactNumber { get; set; }
public Int64 PostalAddressId { get; set; }
public string PostalAddressAddressText { get; set; }
[Required]
public Int64 PostalAddressArea { get; set; }
public string PostalAddressAreaText { get; set; }
public string PostalAddressCode { get; set; }
public Int64 PhysicalAddressId { get; set; }
public string PhysicalAddressAddressText { get; set; }
[Required]
public Int64 PhysicalAddressArea { get; set; }
public string PhysicalAddressAreaText { get; set; }
public string PhysicalAddressCode { get; set; }
}
Here is my simplified Model (destination) class:
public class Paypoint
{
public Int64 Id { get; set; }
public virtual Organisation Organisation { get; set; }
public virtual Employer Employer { get; set; }
public virtual List<EmploymentContract> EmploymentContracts { get; set; }
public bool IsActive { get; set; }
}
public class Organisation
{
public Int64 Id { get; set; }
public virtual List<EmailAddress> EmailAdresses { get; set; }
public virtual List<ContactNumber> ContactNumbers { get; set; }
public virtual List<Address> Adresses { get; set; }
public string RegisteredName { get; set; }
public string TradingName { get; set; }
public string RegistrationNumber { get; set; }
public string WebsiteAddress { get; set; }
}
Here is the code I execute to create mappings in memory on application start:
Mapper.CreateMap<EditPaypointVM, Paypoint>()
.ForMember(dest => dest.IsActive,
opt => opt.UseValue(true))
.ForMember(dest => dest.Organisation,
opt => opt.UseDestinationValue())
.Ignore(i => i.Employer)
.Ignore(i => i.EmploymentContracts);
Upon executing 'AssertConfigurationIsvalid' within a unit test, an "Unmapped error is thrown which states that the Organisation member of Paypoint is unmapped.
Any ideas on what causes this?

Categories