Create Map for IEnumerable to IEnumerable /List in automapper - c#

I am having issue with Automapper when i have a collection.
Below is my situaion
Bussiness Model
public class Member
{
public int MemberId { get; set; }
public int TeamID { get; set; }
public Team Team { get; set; }
}
and i want to return below model from controller
public class Member
{
public int MemberId { get; set; }
public string Team { get; set; }
public int TeamId { get; set; }
}.
I have trying for something like below but not Team string remains null. I beleive i have to specify the mapping. But i am new to automapper i can't figure it out.
IEnumerable<Models.Member> ienumerableDest = _mapper.Map<IEnumerable<Entities.Member>, List<Models.Member>>(members);
Here is the Response

My guess is you haven't specified anywhere how to map your classes.
In your Entities.Member class you have a Team property of type Team, but you are trying to map it to string, so it fails.
In your configuration, add a profile which specifies this mapping, like this:
public class AppProfile : Profile
{
public AppProfile()
{
CreateMap<Entities.Member, Model.Member>(MemberList.Destination)
.ForMember(d => d.Team, opt => opt.MapFrom(src => src.Team.Name));
}
}
In this example i assumed a property "Name" in your Team object.
And in your startup class you should register your profile in your AutoMapper configuration.

Related

Mapping nested lists with automapper

This is my class setup. How do i map only Invalid=false for DTOReportObservation AND DTOReportObservationLocation items?
reports = Mapper.Map<List<Report>, List<DTOReport>>(userReports);
public class DTOReport
{
public List<DTOReportObservation> Observations;
}
public class DTOReportObservation
{
public Guid ReportObservationID { get; set; }
public Guid ReportID { get; set; }
public bool Invalid { get; set; }
public List<DTOReportObservationLocation> ObservationLocations;
}
public class DTOReportObservationLocation
{
public Guid ReportObservationLocationID { get; set; }
public Guid ReportObservationID { get; set; }
public bool Invalid { get; set; }
}
CreateMap<Report, DTOReport>(MemberList.Source)
.ForMember(d => d.Observations, opt => opt.MapFrom(src => src.ReportObservations))
//??ReportObservations.Locations
With automapper you shouldn't need to create maps of lists. You just create a map from one type to another and let automapper iterate over the collections.
Can you also clarify what you mean by Invalid=false seeing as Invalid is a guid type.
For mapping only when invalid is false you can use the conditional mapping. https://automapper.readthedocs.io/en/latest/Conditional-mapping.html .
For more info on lists see here in the docs about collections. https://automapper.readthedocs.io/en/latest/Lists-and-arrays.html

Getting related data via an AutoMapper mapping?

I want to create a mapping between this entity model:
public class ProductType
{
public int Id { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public ICollection<ProductIdentifierInType> Identifiers { get; set; }
public ICollection<ProductPropertyInType> Properties { get; set; }
public ICollection<Product> Products { get; set; }
}
... and this viewmodel:
public class ViewModelProductType
{
public int Id { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public IList<ViewModelProductIdentifier> Identifiers { get; set; }
public IList<ViewModelProductProperty> Properties { get; set; }
public ICollection<ViewModelProduct> Products { get; set; }
}
... but since the Identifiers and Properties are not of the same type in the viewmodel as in the entity model, it won't work directly, like this:
CreateMap<ProductType, ViewModelProductType>();
I don't want to change my models too much. In the entity model, I need the Identifiers and Properties to be respectively ProductIdentifierInType and ProductPropertyInType, because there are many-to-many relationships there, which requires linking tables.
But in the viewmodel, I need Identifiers and Properties to be the full objects in order to display their properties in the view.
Is there a way to accomplish this with mapping? Maybe using .ForPath() to get the two objects' properties?
Assuming you have defined the direct entity to view model mappings:
CreateMap<ProductIdentifier, ViewModelProductIdentifier>();
CreateMap<ProductProperty, ViewModelProductProperty>();
Now it would be enough to extract the corresponding member using LINQ Select inside MapFrom expression. The important thing to know is that AutoMapper does not require the type of the returned expression to match the type of the destination. If they don't match, AutoMapper will use the explicit or implicit mappings for that types.
CreateMap<ProductType, ViewModelProductType>()
.ForMember(dst => dst.Identifiers, opt => opt.MapFrom(src =>
src.Identifiers.Select(link => link.Identifier)))
.ForMember(dst => dst.Properties, opt => opt.MapFrom(src =>
src.Properties.Select(link => link.Property)))
;
I think what you are looking for is a Custom Value Resolver.
There you can explicitly specify how Auto Mapper should map one object to another.
In your case it could look something like this:
public class CustomResolver : IValueResolver<ProductType, ViewModelProductType, IList<ViewModelProductIdentifier>>
{
public int Resolve(ProductType source, ViewModelProductType destination, IList<ViewModelProductIdentifier> destMember, ResolutionContext context)
{
// Map you source collection to the destination list here and return it
}
}
You can then pass/inject the resolver when calling CreateMap, i.e.:
CreateMap<ProductType, ViewModelProductType>()
.ForMember(dest => dest.Identifiers, opt => opt.ResolveUsing<CustomResolver>());
Analogously, do the same for your 'Properties' property.
Note that I did not debug this but merely adapted the examples provided in the link above.

AutoMapper mapping nested List object does not mapping correctly

I need to map DTOs object into my entity object.mapping DTO object into Entity object with one to many relationships it's not working.however when mapping single DTO object to single Entity object its work fine.
Entities -
public class EntityClass
{
[Key]
public int Id { get; set; }
public decimal MonthlyPricing { get; set; }
public virtual IEnumerable<DynamicField> DynamicFields { get; set; }
}
public class DynamicField
{
public int Id { get; set; }
[ForeignKey("Service")]
public int ServiceId { get; set; }
public virtual Service Service { get; set; }
}
My DTO is below ,
public class DTO_Object
{
public int Id { get; set; }
[Required]
public decimal MonthlyPricing { get; set; }
public IEnumerable<DynamicFieldForm> DynamicFields { get; set; }
}
public class DynamicFieldForm
{
public int Id { get; set; }
public int ServiceId { get; set; }
}
my mapping is below,
var config = new MapperConfiguration(cfg => cfg.CreateMap<DTO_Object,
EntityClass>().ForMember(s => s.DynamicFields,o => o.MapFrom(s =>
s.DynamicFields.Select(m => m.Id))));
IMapper imapper = config.CreateMapper();
var service = imapper.Map<DTO_Object, EntityClass>(sourse);
Auto Mapper is not good habit ! It is better that you should build your DBContext manually to link your model's data which are/is encapsulated into the class model.
Again build your Entity Context manually DO NOT USE AutoMapper it is a very
bad habit you can check out this on web resources why is better to write
you Entity context manually.
If you want Entity Data class generate for you you can do it Code First from Database and when you model(s) will be created also Entity DataContext will be created in parallel and with correct mapping then you make correction(Adding Models, Deleting something, Editing).
P.S. For example when i am building something for Enterprise Apps(WebAPI or Web back end) I DO NOT USE AUTO MAPPING. I am coding everything myself.

Automapper - How to map from source child object to destination

I am trying to map from a child object of source to destination(as parent object).
Source Model:
public class SourceBaseResponse<T> where T : new()
{
public string Type { get; set; }
public string Id { get; set; }
public T Attributes { get; set; }
}
For my example I am using T to be of type SourceAssignment
public class SourceAssignment
{
public string Id { get; set; }
public string Email { get; set; }
public string EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTimeOffset CreatedAt { get; set; }
}
Destination Object
public class DestinationAssignment
{
public string Id { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
I want to map Source Model directly to Destination. So, I was trying to use
CreateMap<SourceAssignment, DestinationAssignment>();
CreateMap<SourceBaseResponse<SourceAssignment>, DestinationAssignment>()
.ForMember(dest => dest, opt => opt.MapFrom(src => AutoMapperConfig.Mapper.Map<DestinationAssignment>(src.Attributes)));
This is not working as I am getting run time error in the above line that "Custom configuration for members is only supported for top-level individual members on a type."
So, as per this thread I tried the following
CreateMap<SourceBaseResponse<SourceAssignment>, DestinationAssignment>()
.AfterMap((src, dst) => Mapper.Map(src.Attributes, dst));
Now, I am getting error where mapping should happen which says "Mapper not initialized. Call Initialize with appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance."
I am able to use ForMember for each property and map it from src.Attributes to dest(For eg: src.Attribute.Id to dest.Id). This works, but I do not really want to do this as my Source are complex classes involving nested childs(as this is a Web API response and I do not have control over this). So a lot of custom mapping is done here
CreateMap<SourceAssignment, DestinationAssignment>();
Any suggestions on how to proceed.
Resolution context is needed to be able to call Mapper.Map(), you can get resolution context by using ConstructUsing():
CreateMap<SourceChild, Destination>();
CreateMap<Source, Destination>()
.ConstructUsing((src, ctx) => ctx.Mapper.Map<Destination>(src.SourceChild));

Translating Entity Framework model navigation properties into DTOs

I’m currently working on an n-tier web project. After researching into Data Transfer Objects and their benefits we decided to give this pattern a go. Our ASP.NET MVC website does not have direct access to the EF DbContext but instead will use DTOs to send and receive entity data. There will be a service/mapping layer that will convert between DTOs and entity models.
My question is, what is the best way to translate entity model navigation properties into its DTO?
Below is an example of a entity model and its DTO from the project:
Entity Model:
public class Payment
{
public int ID { get; set; }
public DateTime? PaidOn { get; set; }
public decimal Amount { get; set; }
public string Reference { get; set; }
//Navigation Properties
public virtual PaymentMechanism PaymentMechanism { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
DTO:
public class PaymentDto
{
public int ID { get; set; }
public DateTime? PaidOn { get; set; }
public decimal Amount { get; set; }
public string Reference { get; set; }
//--------Navigation Properties - Object Ids--------
public int PaymentMechanismId { get; set; }
public ICollection<int> OrderIds { get; set; }
}
As can be seen they are very similar except for the navigation properties. I have changed them to hold integer Ids (of the entities) instead of the entity models. Therefore if the navigation property entities need to be obtained, their Id’s can passed into a service/mapping layer function which will retrieve the entities from then database, map them to DTOs and return the collection. Is this an acceptable way of doing things?
I am new to this area so some of my terminology might not be totally correct but hopefully you’ll understand what I'm getting at. If you need me to clarify or provide additional detail on anything, please let me know.
You can load the DTOs using a projection:
var paymentDtos = context.Payments
.Where(p => p.Amount >= 1000m) // just an example filter
.Select(p => new PaymentDto
{
ID = p.ID,
PaidOn = p.PaidOn,
Amount = p.Amount,
Reference = p.Reference,
PaymentMechanismId = p.PaymentMechanism.ID,
OrderIds = p.Orders.Select(o => o.ID)
})
.ToList();
You have to declare the OrderIds in the dto as IEnumerable<int> though, not as ICollection<int> to make this compile.
I'm not sure if this key collection is really useful. If you want to load the orders later you could do it in a separate service method just based on the ID of the Payment, like so:
public IEnumerable<OrderDto> GetPaymentOrders(int paymentID)
{
return context.Payments
.Where(p => p.ID == paymentID)
.Select(p => p.Orders.Select(o => new OrderDto
{
ID = o.ID,
//etc. mapping of more Order properties
}))
.SingleOrDefault();
}
I'm usually using Automapper for this kind of scenario. I would create a Dto class form my main entity and also Dto's for my navigation property entities, then let Automapper do the mapping automatically, without having to write the mapping code manually.
public class PaymentDto
{
public int ID { get; set; }
public DateTime? PaidOn { get; set; }
public decimal Amount { get; set; }
public string Reference { get; set; }
//Navigation Properties
public virtual PaymentMechanismDto PaymentMechanism { get; set; }
public virtual ICollection<OrderDto> Orders { get; set; }
}
public class PaymentMechanismDto
{
//properties
}
public class OrderDto
{
//properties
}
public class MappingProfile : Profile
{
public MappingProfile()
{
Mapper.CreateMap< Payment, PaymentDto >();
Mapper.CreateMap< PaymentMechanism, PaymentMechanismDto >();
Mapper.CreateMap< Order, OrderDto >();
}
}

Categories