Automapper not copy list object to another - c#

I have list of my class type which I need to copy to another class type. In following code I am trying to copy GenderEntity to Gender but getting no results
private void MapGender()
{
List<GenderEntity> _GenderEntity = _GenderServiceObject.GenderEntity();
List<Gender> _Gender = new List<Gender>();
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<List<GenderEntity>, List<Gender>>();
});
IMapper mapper = config.CreateMapper();
_Gender = mapper.Map<List<GenderEntity>, List<Gender>>(_GenderEntity);
var x = "d";
}
GenderEntity - Source
public class GenderEntity
{
public GenderEntity() { }
public string Code { get; set; }
public string Description { get; set; }
}
Gender - Destination
[DataContract]
public class Gender
{
[DataMember]
public string Code { get; set; }
[DataMember]
public string Description { get; set; }
}
Gender entity is used for WCF SOAP services as data contract
Return List Class
public class GenderService
{
#region EBS Service return 'Gender'
public Gender GenderEBSEntity()
{
return GenderEBS.GetGender_EBS();
}
#endregion
#region Mapped Gender Entity
public List<GenderEntity> GenderEntity()
{
return GenderEBS.GetGenderEntity();
}
#endregion
}

You don't need to map Lists with AutoMapper, just create a map like:
cfg.CreateMap<GenderEntity, Gender>();
and then map it like this:
_Gender = mapper.Map<Gender[]>(_GenderEntity).ToList();

my answer...
private void MapGender()
{
List<Gender> MappedGender = new List<Contracts.Data.Gender>();
IList<GenderEntity> genderEntity = _GenderServiceObject.GenderEntity();
if(genderEntity!=null)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<GenderEntity, Gender>();
});
IMapper mapper = config.CreateMapper();
gender = mapper.Map<IList<GenderEntity>, IList<Gender>>(genderEntity).ToList();
}
}

Related

How to set up Automapper global options to allow destination values to be null when source is null

I have next classes where I want to use Automapper and if source property is null, then set destination property as null as well:
public class CompanyTest
{
public Guid? Id { get; set; }
public string CompanyName { get; set; } = string.Empty;
public PersonTest? CEO { get; set; }
public List<PersonTest> People { get; set; } = new();
}
public class CompanyPatchTest
{
public string? CompanyName { get; set; }
public PersonPatchTest? CEO { get; set; }
public List<PersonPatchTest>? People { get; set; }
}
public class PersonTest
{
public Guid? Id { get; set; }
public string Name { get; set; }
public List<PersonTest> Children { get; set; } = new();
}
public class PersonPatchTest
{
public string? Name { get; set; }
public List<PersonPatchTest>? Children { get; set; }
}
and I have created next xunit test that is failing:
using Xunit;
using AutoMapper;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
namespace UnitTests.Tests;
public class AutomapperTests
{
private readonly IMapper _mapper;
public AutomapperTests()
{
var services = new ServiceCollection();
services.AddAutoMapper(x =>
{
x.AddProfile(new ProfileTest());
});
var serviceProvider = services.BuildServiceProvider();
_mapper = serviceProvider.GetRequiredService<IMapper>();
}
[Fact]
public void ShouldSetNullValue()
{
var company = new CompanyTest
{
Id = Guid.NewGuid(),
CompanyName = "OriginalName",
CEO = new()
{
Id = Guid.NewGuid(),
Name= "Name",
Children = new()
{
new PersonTest
{
Id = Guid.NewGuid(),
Name= "Name",
}
}
},
};
var source = new CompanyPatchTest
{
CompanyName = "CompanyName",
CEO = null,
};
company = _mapper.Map<CompanyPatchTest, CompanyTest>(source, company);
company.CEO.Should().BeNull();
}
}
My profile is the next one:
public class ProfileTest : Profile
{
public ProfileTest()
{
CreateMap<CompanyTest, CompanyPatchTest>();
CreateMap<PersonTest, PersonPatchTest>();
CreateMap<CompanyPatchTest, CompanyTest>(MemberList.Source)
.ForMember(d => d.CEO, op => op.AllowNull());
CreateMap<PersonPatchTest, PersonTest>(MemberList.Source);
}
}
If I remove CreateMap<PersonPatchTest, PersonTest>(MemberList.Source); line then it works. But I won't be able to customize that mapping.
Also, it'd be nice if this could be set up as a global setting for all properties.
I have tried using AllowNullDestinationValues but it is not working
...
public AutomapperTests()
{
var services = new ServiceCollection();
services.AddAutoMapper(x =>
{
x.AddProfile(new ProfileTest());
x.AllowNullCollections = true;
x.AllowNullDestinationValues = true;
});
var serviceProvider = services.BuildServiceProvider();
_mapper = serviceProvider.GetRequiredService<IMapper>();
}
...
If this cannot be done using Automapper, do you know any other tool that ?could achieve this.
Autmapper version: 12.0.0
This is a bug that was solved in version 12.0.1. You don't need any settings for that test to pass. The default for AllowNullDestinationValues is true.
https://github.com/AutoMapper/AutoMapper/pull/4083

Automapper setting property to null when it should ignore

I was trying to implement a code to ignore a property (therefore mantaining the source value). I used the ignore method, which works most of the time. For some reason I noticed that sometimes the ignore sets the property value to null.
Do you know what could be problem?
I created the following code to reproduce the issue. I was expecting client.ContactDetails.First().Address to have the value "Old".
using AutoMapper;
class Program
{
static void Main(string[] args)
{
ClientMapperProfile clientMapperProfile = new ClientMapperProfile();
var configurationProvider = new MapperConfiguration(c => c.AddProfile(clientMapperProfile));
Mapper mapper = new Mapper(configurationProvider);
var client = new Client()
{
ContactDetails = new []
{
new ContactDetails()
{
Address= "Old"
}
}
};
var clientDto = new ClientDto()
{
ContactDetails = new []
{
new ContactDetailsDto()
{
Address = "New"
}
}
};
mapper.Map(clientDto,client);
Console.WriteLine(client.ContactDetails.First().Address);
}
}
public class Client
{
public ContactDetails[] ContactDetails { get; set; }
}
public class ContactDetails
{
public string Address { get; set; }
}
public class ClientDto
{
public ContactDetailsDto[] ContactDetails { get; set; }
}
public class ContactDetailsDto
{
public string Address { get; set; }
}
public class ClientMapperProfile : Profile
{
public ClientMapperProfile()
{
CreateMap<ClientDto, Client>();
CreateMap<ContactDetailsDto, ContactDetails>()
.ForMember(c => c.Address, opt => opt.Ignore());
}
}

Maping generic interface to concreate class but using interface definition -> automapper

Hey I'm trying to map my generic class to concrete class but using it's interface.
My service returns me data which type is
IPaggedResults<Customer>
and I want to be able to map this to
IPaggedResults<CustomerDto>
It works if I invoke mapping with:
_mapper.Map<PaggedResults<CustomerDto>>
but I want use following syntax:
_mapper.Map<IPaggedResults<CustomerDto>>
public class PaggedResults<T> : IPaggedResults<T>
{
public IEnumerable<T> Results { get; protected set; }
public int TotalResults { get; protected set; }
public int TotalPages { get; protected set; }
public int ResultsPerPage { get; protected set; }
public PaggedResults(IEnumerable<T> results, int totalResults, int resultsPerPage)
{
Results = results;
TotalResults = totalResults;
TotalPages = totalResults / resultsPerPage;
ResultsPerPage = resultsPerPage;
}
}
public class CustomerDto
{
public int Id { get; set; }
public string Name { get; set; }
public string NIP { get; set; }
}
My mapper configuration:
public static IMapper Initialize()
=> new MapperConfiguration(cfg =>
{
cfg.CreateMap<CustomerCompany, CustomerDto>();
cfg.CreateMap(typeof(IPaggedResults<>), typeof(PaggedResults<>));
cfg.CreateMap(typeof(IPaggedResults<>), typeof(IPaggedResults<>)).As(typeof(PaggedResults<>));
}).CreateMapper();
Im'using Automapper by Jimmy Bogard.
I could achieve it through the following code:
Create an extension for IMapperConfigurationExpression
public static class IMapperConfigurationExpressionExtensions
{
public static void MapPaggedResults<TSource, TDestination>(this IMapperConfigurationExpression exp){
exp.CreateMap(typeof(PaggedResults<TSource>), typeof(IPaggedResults<TDestination>))
.ConstructUsing((source, ctx) => { return ctx.Mapper.Map<PaggedResults<TDestination>>(source) as IPaggedResults<TDestination>; });
}
}
Then use this configuration:
public static IMapper Initialize()
=> new MapperConfiguration(cfg =>
{
cfg.CreateMap(typeof(IPaggedResults<>), typeof(PaggedResults<>));
cfg.MapPaggedResults<CustomerCompany, CustomerDto>();
}).CreateMapper();
Then both results can be obtained:
var _mapper = Initialize();
IPaggedResults<CustomerCompany> source = new PaggedResults<CustomerCompany>(
new List<CustomerCompany>() { new CustomerCompany() {Id =42, Name = "SomeName", NIP = "someNIP" } }, 1, 1);
var resut = _mapper.Map<PaggedResults<CustomerDto>>(source);
var resut2 = _mapper.Map<IPaggedResults<CustomerDto>>(source);

Automapper configuration with cast in field name

Why this mapping in the example is not map correctly. When I change the suffix CAST to something else (CAST1) it maps correctly. How should I set the mapper to map correctly with the suffix CAST?
public class Class1
{
public string OBEC { get; set; }
public string OBEC_CAST { get; set; }
}
public class Class2
{
public string Obec { get; set; }
public string ObecCast { get; set; }
}
public void Map()
{
Class1 c1 = new Class1() { OBEC = "obec", OBEC_CAST = "cast obce" };
Class2 c2 = Mapper.Map<Class2>(c1);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
Mapper.Initialize(cfg =>
{
cfg.RecognizePrefixes(new string [0]);
cfg.RecognizePostfixes(new string[0]);
cfg.ClearPrefixes();
cfg.SourceMemberNamingConvention = new LowerUnderscoreNamingConvention();
cfg.DestinationMemberNamingConvention = new PascalCaseNamingConvention();
cfg.CreateMap<Class1, Class2>();
});
}

Pass additional object to CreateMap

My object mapping requires me to pass the source object and an additional object to be able to map the destination object appropriately. However, I am unable to determine a way to be able to get that done.
public class SourceDto
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string SourceValue3 { get; set; }
public string SourceValue4 { get; set; }
}
public class AnotherSourceDto
{
public string AnotherSourceValue1 { get; set; }
public string AnotherSourceValue2 { get; set; }
public string AnotherSourceValue3 { get; set; }
public string AnotherSourceValue4 { get; set; }
}
public class Destination
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string DestValue3 { get; set; }
}
public string ConvertToDestValue3(string sourceValue3, string anotherSourceValue2)
{
// Some logic goes here
return sourceValue3 + " " + anotherSourceValue2;
}
void Main()
{
var config = new MapperConfiguration(
cfg =>cfg.CreateMap<SourceDto, Destination>()
.ForMember(dest => dest.DestValue3,
opt => opt.MapFrom(
src => ConvertToDestValue3(src.SourceValue3, "" //Here I need to pass AnotherSourceDto.AnotherSourceValue2 ))));
}
I'm afraid the answer is to map that property outside AutoMapper.
Here's an example where you could do this using an extension method, so it still has the look and feel of being done with AutoMapper.
// put inside a static class
public static Destination CustomMap(
this IMapper mapper,
SourceDto source,
AnotherSourceDto anotherSource)
{
var destination = mapper.Map<Destination>(source);
destination.DestValue3 =
source.SourceValue3 + " " + anotherSource.AnotherSourceValue2;
return destination;
}
void Main()
{
var config = new MapperConfiguration(cfg =>
cfg.CreateMap<SourceDto, Destination>()
.ForMember(dest => dest.DestValue3, opt => opt.Ignore()));
// usage
var mapper = config.CreateMapper();
var source = new SourceDto { /* add properties */ };
var anotherSource = new AnotherSourceDto { /* add properties */ };
var destination = mapper.CustomMap(source, anotherSource);
}

Categories

Resources