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
Related
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>();
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.
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)
We are designing a temporal system where the definition of an entity can change. I am trying to setup Automapper but can't quite work out how the prefix should work.
As an example, I would have the following entity:
public class ReferenceDataDefinition
{
public string Name { get; set; }
}
public class ReferenceData
{
public int Id { get; set; }
public ReferenceDataDefinition Current { get; set; }
}
With the following DTO:
public class ReferenceDataDTO
{
public int Id { get; set; }
public string Name { get; set; }
}
I know I can use
CreateMap<ReferenceData, ReferenceDataDTO>()
.ForMember(p => p.Id, o => o.MapFrom(s => s.Id)
.ForMember(p => p.Name, o => o.MapFrom(s => s.Current.Name);
But I feel there must be something smarter I can do?
I've tried adding RecognizePrefixes("Current") but that had no effect.
I've tried adding RecognizePrefixes("Current")
This isn't how prefix are used. They are for a scenario where your properties start with a prefix (often because of a database naming schema).
For example, If you had the following classes:
public class ReferenceData
{
public int Ref_Id { get; set; }
public string Ref_Name { get; set; }
}
public class ReferenceDto
{
public int Id { get; set; }
public string Name { get; set; }
}
You could recognize the following prefix:
cfg.RecognizePrefixes("Ref_");
AutoMapper would then be able to map those two objects without you having to define specific mappings with .ForMember.
Regarding you own mapping, since both Id properties on ReferenceData and ReferenceDataDTO have the same name, you should be able to remove the Id member mapping as AutoMapper can infer it automatically:
CreateMap<ReferenceData, ReferenceDataDTO>()
.ForMember(p => p.Name, o => o.MapFrom(s => s.Current.Name);
This should suffice.
As for .Current using Flattening you could remove it if you would change your DTO class to rename it to CurrentName.
Please check this documentation:
Recognizing pre/postfixes
Also the RecognizePrefixes works for source object prefixes
Use RecognizeDestinationPrefixes method
Check these previous posts:
AutoMapper with prefix
https://github.com/AutoMapper/AutoMapper/issues/421
I have three lists in an object that all use the same variable, right now to build the one to many relationship I'm using:
modelBuilder
.Entity<Object>()
.HasMany(u => u.ListA)
.WithRequired()
.HasForeignKey(s => s.ObjectId);
modelBuilder
.Entity<Object>()
.HasMany(u => u.ListB)
.WithRequired()
.HasForeignKey(s => s.ObjectId);
modelBuilder
.Entity<Object>()
.HasMany(u => u.ListC)
.WithRequired()
.HasForeignKey(s => s.ObjectId);
The list item has a type property that corresponds to list A B and C, Is there a way to further refine those statements to add something similar to :
where (t=>t.type == A)
Object has 3 lists that all hold the same object type the only difference between the list items is their type property.
list object properties:
public int Id { get; set; }
public EntryType Type { get; set; }
public string Value { get; set; }
public int SequenceNumber { get; set; }
public virtual StatusReport StatusReport { get; set; }
public int StatusReportId { get; set; }