Automapper Config: Can I use .ForAllMembers when overriding CreateMap? - c#

public class MyProfile : Profile
{
protected override void Configure()
{
base.CreateMap<ViewModel, Domain>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
//.ForAllMembers(opt => opt.Ignore()) //returns void
.ReverseMap();
}
}
public class ViewModel
{
public int Id { get; set; }
}
public class Domain
{
public int Id { get; set; }
public string UserName {get; set;}
//public string ... { get; set;} //etc..
//...
}
Suppose I don't want to map UserName, and many other properties.
Can I do .ForAllMembers(...) to the mapping, in order to map any unmapped members?

Don't use that ForAllMembers thing, that looks like a version of this:
https://github.com/AutoMapper/AutoMapper/wiki/5.0-Upgrade-Guide#ignoreallnonexisting-extension
Instead, use the CreateMap overload that takes a MemberList enum:
CreateMap<ViewModel, Domain(MemberList.None)

Related

AutoMapper relations between two entities

I have two entity models in my ASP.NET Core 6 application:
public partial class Employee
{
public int Id { get; set; }
public string SerialNumber { get; set; } = null!;
public bool Active { get; set; }
public int? FKIdClassEmployee { get; set; }
public virtual ClassEmployee? ClassEmployee { get; set; }
}
public partial class ClassEmployee
{
public ClassEmployee()
{
Employee = new HashSet<Employee>();
}
public int Id { get; set; }
public string? Label { get; set; }
public decimal? Cost { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
In the EmployeeController, I'm try to use AutoMapper to map ClassEmployee to an existing record.
This is my Automapper config:
public class EmployeeProfile : Profile
{
private readonly ApplicationContext _ctx;
public EmployeeProfile(ApplicationContext ctx)
{
_ctx = ctx;
EmployeeDTOToEmployee();
}
private void EmployeeDTOToEmployee()
{
CreateMap<EmployeeDTO, Employee>()
.ForMember(dest => dest.SerialNumber, opt => opt.MapFrom(d => d.SerialNumber))
.ForMember(dest => dest.Active, opt => opt.MapFrom(d => d.Active))
.ForMember(dest => FKIdClassEmployee, opt => opt.MapFrom(d => d.FKIdClassEmployee))
.ForMember(dest => dest.ClassEmployee, opt => opt.MapFrom(
src => _ctx.ClassEmployee.Find(src.FKIdClassEmployee)));
}
}
But it doesn't work because it returns an error
Duplicate key value violates unique constraint
when it is called the Create method.
First of all involving DbContext in mapping configuration isn't a good idea (in my opinion). Use Lazy, Eager or Explicit loading instead. If a pair of Model (Entity) and Dto has same property name and type you don't have to specify .ForMemeber(dest => dest..., opt => opt.MapFrom(...)) . For nested mapping just configure all Entities and Dtos.
Configuration should look like this:
// NOTE: Same property type and name will be mapped
// Map Dto values to Entity
CreateMap<EmployeeDTO, Employee>();
CreateMap<ClassEmployeeDTO, ClassEmployee>();
// Map Entity values to Dto
CreateMap<Employee, EmployeeDTO>();
CreateMap<ClassEmployee, ClassEmployeeDTO>();

how to map slightly complicated scenario then from -> to member scenario

I have a CarDto and Car domain object.
Car has list of Drivers.
public class Car
{
public int Id {get; set; }
public int Name {get; set; }
public ICollection<Driver> Drivers {get; set; }
}
public class CarVM
{
public int Id {get; set; }
public int Name {get; set; }
public string DriverBadge {get; set; }
public string[] Drivers {get; set; }
}
I'm trying to map this objects using AutoMapper
CreateMap<CarVM, Car>()
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name))
.ForMember(dest => dest.Drivers, opts => {
... how to map here?
})
All you have to do is map from string[] to List<>. Simply use the below
CreateMap<CarVM, Car>()
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name))
.ForMember(dest => dest.Drivers, opts => opts.MapFrom(s=> s.Drivers.ToList()))
});
More information - Automapper Lists & Arrays

Automapper ignore nested collection properties

I have a simple objects:
public class Project : Entity
{
public uint ProjectId { get; set; }
public virtual ICollection<Cabin> Cabins { get; set; }
}
public class Cabin : Entity
{
public IPAddress IpAddress { get; set; }
public int Port { get; set; }
public DateTime LastConnection { get; set; }
public byte ConnectionStatus { get; set; }
public byte TechnicalStatus { get; set; }
public Project Project { get; set; }
public int ProjectId { get; set; }
}
So mapping using auto mapper from one to another with some ignores would look like:
var mapperConfig = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Project, Project>()
.ForMember(source => source.Id, opt => opt.Ignore())
.ForMember(source => source.ProjectId, opt => opt.Ignore())
.ForMember(source => source.Cabins, opt => opt.MapFrom(cab => cab.cabins));
});
And it works it maps one project object to another, and ignores id and project id and maps collection.
But on that level, is it possible to set what properties from source.Cabins would be ignored?
For example i want to ignore ConnectionStatus, TechnicalStatus.
You could add a configuration mapping for Cabin entity and AutoMapper would look at these configurations before mapping Cabin entity.
cfg.CreateMap<Cabin, Cabin>()
.ForMember(source => source.ConnectionStatus, opt => opt.Ignore())
.ForMember(source => source.TechnicalStatus, opt => opt.Ignore());
Or you could use AfterMap event to define a default value for these properties.

Automapper Ignore complex properties

Im using Automapper v6.2 with profiles and Autofac to register those profiles, my Domain classes have properties that my Models does not have as I dont need them (For example an Id or other properties that only needs to be saved on my dB)
For example:
public class TurnoProfile : Profile
{
public TurnoProfile()
{
CreateMap<Turnos, TurnoModel>()
.ForMember(d => d.Id, s => s.MapFrom(src => src.Id))
.ForMember(d => d.Descripcion, s => s.MapFrom(src => src.Descripcion));
CreateMap<TurnoModel, Turnos>()
.ForMember(d => d.Id, s => s.MapFrom(src => src.Id))
.ForMember(d => d.Descripcion, s => s.MapFrom(src => src.Descripcion))
.ForMember(d => d.Alumnos, s => s.Ignore());
}
}
Domain object (EF 5)
public partial class Turnos
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Turnos()
{
Alumnos = new HashSet<Alumnos>();
}
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Descripcion { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Alumnos> Alumnos { get; set; }
}
Model:
public class TurnoModel
{
#region Fields
public int Id { get; set; }
public string Descripcion { get; set; }
#endregion
}
As you can see I dont want Alumnos to be mapped, this is a 1 to many relationship, I want to ignore which Alumnos have certain Turnos.
But I have the following error:
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters
============================================================
TurnoModel -> Turnos (Destination member list)
Prog_II.Data.Model.TurnoModel -> Prog_II.Data.Domain.Turnos (Destination member list)
Unmapped properties:
Alumnos

EF5 code-first Derived class foreign key/reference issue

I have the following:
public abstract class InputBase
{
public virtual ICollection<Data> Data { get; set; }
}
public class InputA: InputBase { }
public class InputB: InputBase { }
public abstract class Data
{
public virtual InputA InputA { get; set; }
public virtual InputB InputB { get; set; }
}
InputA and InputB both use InputBase's collection of Data.
Data will have an instance of InputA and InputB at all times.
I tried linking this up via:
modelBuilder.Entity<Data>()
.HasRequired(data => data.InputA)
.WithMany(input => input.Data)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Data>()
.HasRequired(data => data.InputB)
.WithMany(input => input.Data)
.WillCascadeOnDelete(false);
modelBuilder.Entity<InputA>()
.HasRequired(input => input.Data)
.WithRequired(data => data.InputA)
.WillCascadeOnDelete(false);
modelBuilder.Entity<InputB>()
.HasRequired(input => input.Data)
.WithRequired(data => data.InputB)
.WillCascadeOnDelete(false);
However, I'm getting a MetaDataException, error 0040: Type Data_InputA is not defined in namespace (...)
How can I make this work? I don't want to have to create separate Data collections on the Input derivates as that wouldn't be logically correct.
This should fix your problem:
Change this:
public abstract class Data
{
public virtual InputA InputA { get; set; }
public virtual InputB InputB { get; set; }
}
To: If you are worried about the foreign key, its alright. Key of InputA Object is the same as InputBase Object since we are using Inheritance. Hence, Data Structure dictates that we do the following.
public abstract class Data
{
[ForeignKey("InputBase"), DatabaseGenerated(DatabaseGeneratedOption.None)]
public int? InputBaseId { get; set; }
public virtual InputBase InputBase { get; set; }
}
If your Inheritance is Table per Type (TPT),
Do this to derived classes:
[Table("InputB")] //This is what your table will be named in your database for derived class
public class InputA: InputBase { }
[Table("InputB")]
public class InputB: InputBase { }
Also you don't need this:
modelBuilder.Entity<Data>()
.HasRequired(data => data.InputA)
.WithMany(input => input.Data)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Data>()
.HasRequired(data => data.InputB)
.WithMany(input => input.Data)
.WillCascadeOnDelete(false);
modelBuilder.Entity<InputA>()
.HasRequired(input => input.Data)
.WithRequired(data => data.InputA)
.WillCascadeOnDelete(false);
modelBuilder.Entity<InputB>()
.HasRequired(input => input.Data)
.WithRequired(data => data.InputB)
.WillCascadeOnDelete(false);
You can remove that section because we have used Property Mapping.

Categories