I have a model which I am trying to map from Match class in .net core 2.0.
Both the classes have a Name property.
I need to map Match.Value => ViewCompany.Name
But it always puts Match.Name into ViewCompany.Name
Here is my AutomapperProfile:
CreateMap<Match, ViewCompany>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Value));
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Value))
ViewCompany:
public class ViewCompany
{
public ViewCompany()
{
}
public ViewCompany(string name)
{
this.Name = name;
}
public int Id { get; set; }
public string Name { get; set; }
}
The above mapping doesn't work.
But if I change property name in the model to something else like "Value" or "tempName" and update the automapper profile, it works fine.
So, is it not possible to map properties with same names to different properties in Automapper?
What happens here is that Name is mapped through the constructor. A simple way to avoid that is to tell AM what constructor to use:
CreateMap<Match, ViewCompany>().ConstructUsing(source=>new ViewCompany());
Related
I have an Object and need to select the Name property and assign it to String.
but it doesn't work!
who to write true Profiles mapping
class Tag
{
public string Name {get;set;}
}
public class TagProfile : ProfileBase
{
public TagProfile()
{
CreateMap<Tag, string>()
.ForMember(dest => dest, opt => opt.MapFrom(src => src.Name));
}
}
you can try it on profile contractor:
CreateMap<Tag, string>()
.ConvertUsing(source => source.Name);
I have an object model something like this:
public class Concert {
public Band Band { get; set; }
public ConcertVenue Venue { get; set; }
}
public class TicketOrder {
public Concert Concert { get; set; }
public string CustomerName { get; set; }
}
// DTOs for email and web views:
public class ConcertDto {
public string Artist { get; set; }
public string Venue { get; set; }
}
public class TicketOrderDto : ConcertDto {
public string CustomerName { get; set; }
}
I'm using AutoMapper to map domain objects to DTOs. The DTOs here have an inheritance relationship that doesn't exist in the domain model (because when I send an email about a ticket order, I want to include all the information about the concert)
I have a mapping defined like this:
config.CreateMap<Concert, ConcertDto>()
.ForMember(dto => dto.Artist, opt => opt.MapFrom(concert => concert.Band.Name))
.ForMember(dto => dto.Venue, opt => opt.MapFrom(concert => concert.GetVenueSummary());
config.CreateMap<TicketOrder, ConcertDto>()
.ForMember(dto => dto.Artist, opt => opt.MapFrom(concert => concert.Band.Name))
.ForMember(dto => dto.Venue, opt => opt.MapFrom(concert => concert.GetVenueSummary())
.ForMember(dto => dto.CustomerName, optn.MapFrom(order => order.Customer.FullName))
;
There's some duplication in those maps, and what I want to do is to reuse the Concert > ConcertViewData mapping when I map the TicketOrderDto:
cfg.CreateMap<TicketOrder, TicketOrderDto>()
// This is what I *want* but isn't valid AutoMapper syntax:
.IncludeMembers(dto => dto, order => order.Concert)
.ForMember(dto => dto.CustomerName, optn.MapFrom(order => order.Customer.FullName));
but this fails with:
System.ArgumentOutOfRangeException: Only member accesses are allowed.
dto => dto (Parameter 'memberExpressions')
at AutoMapper.ExpressionExtensions.EnsureMemberPath(LambdaExpression exp, String name)
Calling .IncludeBase<> doesn't work, because ConcertOrder doesn't derive from Concert.
Is there an easy way to import one map into another but specify that it should map from a child object of the source type? i.e. "hey, please map source.Child onto this, and then run the regular source > this mapping?"
I am going to make an assumption here, but I believe the mapping should be from TicketOrder to TicketOrderDto, and not ConcertDto (which contains no CustomerName property) as the given models don't match the given mapping configuration.
In that case, you should be able to use .AfterMap() on ticket mapper configuration to map from Concert to ConcertDto.
cfg.CreateMap<TicketOrder, TicketOrderDto>()
.ForMember(d => d.CustomerName, o => o.MapFrom(s => s.Order.Customer.Name))
.AfterMap((s, d, context) => context.Mapper.Map(s.Concert, d));
I have a pretty basic Entity Framework entity that looks like this:
public class Student
{
public string Given { get; set; }
public string Surname { get; set; }
public ICollection<Address> Addresses { get; set; }
}
I'd like to use AutoMapper to map this entity to a corresponding flattened ViewModel that looks like this:
public class StudentViewModel
{
public string Given { get; set; }
public string Surname { get; set; }
public string PhysicalAddressStreet { get; set; }
public string PhysicalAddressCity { get; set; }
public string PhysicalAddressState { get; set; }
public string PostalAddressStreet { get; set; }
public string PostalAddressCity { get; set; }
public string PostalAddressState { get; set; }
}
For this I've tried the following mapping configuration:
CreateMap<Student, StudentViewModel>()
.ForMember(dest => dest.Given, opt => opt.MapFrom(src => src.Given))
.ForMember(dest => dest.Surname, opt => opt.MapFrom(src => src.Surname))
.ForMember(dest => dest.PhysicalAddressStreet, opt => opt.MapFrom(src => src.Addresses.FirstOrDefault(add => add.Type == AddressType.Physical).Street))
.ForMember(dest => dest.PhysicalAddressCity, opt => opt.MapFrom(src => src.Addresses.FirstOrDefault(add => add.Type == AddressType.Physical).City))
.ForMember(dest => dest.PhysicalAddressState, opt => opt.MapFrom(src => src.Addresses.FirstOrDefault(add => add.Type == AddressType.Physical).State))
.ForMember(dest => dest.PostalAddressStreet, opt => opt.MapFrom(src => src.Addresses.FirstOrDefault(add => add.Type == AddressType.Postal).Street))
.ForMember(dest => dest.PostalAddressCity, opt => opt.MapFrom(src => src.Addresses.FirstOrDefault(add => add.Type == AddressType.Postal).City))
.ForMember(dest => dest.PostalAddressState, opt => opt.MapFrom(src => src.Addresses.FirstOrDefault(add => add.Type == AddressType.Postal).State));
The problem is, when I run this mapping using projections:
studentDbSet.Where(st => st.Id == studentId)
.ProjectTo<TProjection>(_mapper.ConfigurationProvider);
I get the following error:
Dynamic SQL Error SQL error code = -104 Token unknown - line 14,
column 2 OUTER
This is a Firebird error, it seems that when compiling the Linq to SQL the query that is being generated includes OUTER APPLY, which is not supported in Firebird.
Is there any way to rework my projection to avoid the OUTER APPLY?
To the best of my knowledge, the OUTER APPLY is generated from the FirstOrDefault() call. Is there another way I can write the Linq to avoid using that?
Edit for clarification: This is a situation where I am not in a position to be able to modify the Entity or the database schema, so assume that those are untouchable.
I think you have a modeling problem at the core here. If you need the physical address, just include a PhysicalAddress property on the model, and maintain that relationship. You can still have the collection of addresses with the type. It looks like you're doing "FirstOrDefault", meaning either you can only have one physical address or only the first matters. I'm guessing it's that you can only have one.
So just have one. On the Student model (and Student table), have a FK to the Address table, "PhysicalAddress". Then in the places in the code you maintain addresses, update the PhysicalAddress appropriately. Encapsulating the child collection so that you can't do just any add/remove operation helps.
Once you have a PhysicalAddress relationship on the Student, this problem becomes trivial, it's just a normal mapping.
Here is the only way of writing the LINQ query that avoids OUTER APPLY (not sure how that can be mapped with AutoMapper, leaving that part for you if you really need it):
var query =
from student in studentDbSet
where student.Id == studentId
from physicalAddress in student.Addresses.Where(a => a.Type == AddressType.Physical)
from postalAddress in student.Addresses.Where(a => a.Type == AddressType.Postal)
select new StudentViewModel
{
Given = student.Given,
Surname = student.Surname,
PhysicalAddressStreet = physicalAddress.Street,
PhysicalAddressCity = physicalAddress.City,
PhysicalAddressState = physicalAddress.State,
PostalAddressStreet = postalAddress.Street,
PostalAddressCity = postalAddress.City,
PostalAddressState = postalAddress.State,
};
I have a rather complex situation.
I have an order:
public partial class Order
{
public string orderNum { get; set;}
public ICollection<Shipment> Shipments { get; set; }
...
}
I have a view BIGVIEW:
public partial class BIGVIEWVM
{
public string orderNum { get; set;}
public OrderDetailsVM Order { get; set; }
public ShipmentsTableVM Shipments { get; set; }
}
The subview, ShipmentsTableVM is like so:
public partial class ShipmentsTableVM
{
public string somethingelse {get;set;}
public IEnumerable<Shipment_Table_Row_VM> Shipments{get;set;}
}
It's actually more complex than this, but this is the issue in its simplest form.
I have an Order with lots of Shipments. I want to make a ProjectTo call using AutoMapper from the Order to the BIGVIEW. The entire of the Order is mapped into the OrderDetailsVM with no problem, however, What I do not know how to do is pass the ICollection to my ShipmentsTableVM and THEN map it into the IEnumerable.
cfg.CreateMap<Order, BIGVIEW>()
.ForMember(d => d.order, opt => opt.MapFrom(s => s))
...?
I thought I could take care of the mapping from the ICollection<Shipments> to my ShipmentsTableVM:
cfg.CreateMap<IEnumerable<Shipment>, ShipmentsTableVM>()
.ForMember(d => d.Shipments, opt => opt.MapFrom(s => s))
;
But this doesn't work - I get an error about Linq not being able to map things that are not an IEnumerable... but I am using an IEnumerable so I don't understand. I have tried a variety of other configurations too but I'm getting nowhere fast.
I am looking for an Automapper solution for this, as I do not want to manually do the projection in my controller.
Thanks
There are few ways to achieve this, here is one of them.
cfg.CreateMap<Shipments, Shipment_Table_Row_VM>();
cfg.CreateMap<ICollection<Shipments>, ShipmentsTableVM>()
.ConstructUsing(obj => new ShipmentsTableVM())
.ForMember(d => d.Shipments, opt => opt.MapFrom(s => s));
cfg.CreateMap<Order, BIGVIEWVM>();
You need to pass proper model to subview. try :
Order.Shipments
or
Model.Shipments
I'm having some trouble trying to map these two classes (Control -> ControlVM)
public class Control
{
public IEnumerable<FieldType> Fields { get; set; }
public class FieldType
{
//Some properties
}
}
public class ControlVM
{
public FieldList Fields { get; set; }
public class FieldList
{
public IEnumerable<FieldType> Items { get; set; }
}
public class FieldType
{
//Properties I'd like to map from the original
}
}
I tried with opt.ResolveUsing(src => new { Items = src.Fields }) but apparently AutoMapper cannot resolve the anonymous type. Also tried extending ValueResolver, but didn't work either.
NOTE: This VM is later used in a WebApi, and JSON.NET needs a wrapper around the collection to properly deserialize it. So removing the wrapper is not a solution.
NOTE2: I'm also doing Mapper.CreateMap<Control.FieldType, ControlVM.FieldType>(), so the problem isn't there.
This works for me:
Mapper.CreateMap<Control.FieldType, ControlVM.FieldType>();
// Map between IEnumerable<Control.FieldType> and ControlVM.FieldList:
Mapper.CreateMap<IEnumerable<Control.FieldType>, ControlVM.FieldList>()
.ForMember(dest => dest.Items, opt => opt.MapFrom(src => src));
Mapper.CreateMap<Control, ControlVM>();
Update: Here's how to map the other way:
Mapper.CreateMap<ControlVM.FieldType, Control.FieldType>();
Mapper.CreateMap<ControlVM, Control>()
.ForMember(dest => dest.Fields, opt => opt.MapFrom(src => src.Fields.Items));