Automapper setting property to null when it should ignore - c#

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());
}
}

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

Unable to determine the serialization information when create index

I'm having problem when i create a index.
This is my code:
private static IMongoCollection<IPageItem> GetCollection()
{
return Connection.Database.GetCollection<IPageItem>("SystemPages");
}
internal static IEnumerable<Task> CreateIndex()
{
BsonClassMap.RegisterClassMap<NewsItem>();
var work = new List<Task>();
var coll = GetCollection();
var builder= Builders<IPageItem>.IndexKeys;
var btIndexes = new List<CreateIndexModel<IPageItem>>
{
new(builder.Ascending(x => x.PageId), new CreateIndexOptions { Unique = true })
};
work.Add(coll.Indexes.CreateManyAsync(btIndexes));
return work;
}
public interface IPageItem
{
public Guid PageId { get; set; }
}
public class NewsItem : IPageItem
{
public Guid PageId { get; set; }
public string Summery { get; set; }
}
When i call, CreateIndex() i receive, Unable to determine the serialization information for x => Convert(x.PageId, Object).)
Can I not use one interface when create/get a collection?

One-to-many update in EF Core with value object

I have 3 classes as you can see :
public class car
{
public int Id { set; get; }
public string Name { set; get; }
public List<CarTest> CarTest { get; set; }
public car(string name, int id, List<CarTest> carTests)
{
Id = id;
Name = name;
CarTest = carTests;
}
public car()
{
}
}
public class CarTest
{
public OVTest OV { get; set; }
public string CarOV { get; set; }
}
public class OVTest
{
public string Name { get; set; }
}
I want to edit my value as you can see :
static void Main(string[] args)
{
myctx myc = new myctx();
var cartests = new List<CarTest>();
var cartest = new CarTest();
var OV = new OVTest();
OV.Name = "editedName";
cartest.OV = OV;
cartest.CarOV = "2OV";
cartests.Add(cartest);
var editcar = new car("ee5155fe",2, cartests);
myc.ChangeTracker.TrackGraph(editcar, e =>e.Entry.State = EntityState.Modified);
myc.SaveChanges();
Console.ReadLine();
}
But I get this error :
System.InvalidOperationException: 'The property 'CarTestId' on entity type 'OVTest' is part of a key and so cannot be modified or marked as modified. To change the principal of an existing entity with an identifying foreign key first delete the dependent and invoke 'SaveChanges' then associate the dependent with the new principal.'
This is my config:
public class CarConfig : IEntityTypeConfiguration<car>
{
public void Configure(EntityTypeBuilder<car> builder)
{
builder.OwnsMany(u => u.CarTest);
builder.OwnsMany(u => u.CarTest).OwnsOne(c=>c.OV);
}
}

Automapper not copy list object to another

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();
}
}

AutoMapper: Set all properties of destination object to default if source object is null for specified types

Is it possible to configure AutoMapper to set all properties to default value in case if the source object is null for specified classes? I know that I should use Mapper.AllowNullDestinationValues = false; to do what I want for all classes in application.
Here the sampled code that I use for tests, but it doesn't work
public class A
{
static A()
{
Mapper.Initialize(
config =>
{
config.ForSourceType<B>().AllowNullDestinationValues = false;
config.CreateMap<B, A>()
.ForMember(member => member.Name, opt => opt.Ignore());
});
//Mapper.AllowNullDestinationValues = false;
Mapper.AssertConfigurationIsValid();
}
public void Init(B b)
{
Mapper.DynamicMap(b, this);
}
public int? Foo { get; set; }
public double? Foo1 { get; set; }
public bool Foo2 { get; set; }
public string Name { get; set; }
}
public class B
{
public string Name { get; set; }
public int? Foo { get; set; }
public double? Foo1 { get; set; }
public bool Foo2 { get; set; }
}
Using of this code:
var b = new B() {Foo = 1, Foo1 = 3.3, Foo2 = true, Name = "123"};
var a = new A {Name = "aName"};
a.Init(b); // All ok: Name=aName, Foo=1, Foo1=3,3, Foo2=True
a.Init(null); // Should be Name=aName, Foo=null, Foo1=null, Foo2=False,
// but a has the same values as on a previous line
It must be related to "a" being already mapped.
var a = new A {Name = "aName"};
a.Init(b);
a.Init(null);
All mappings are cached, so if you'll attempt to re-map same instance the automapper would just keep the original result.
In order to test it, try:
var c = new A {Name = "x"};
c.Init(null);
Here is a link to similar question.
Looks like it will replace with null if you set Mapper.Configuration.AllowNullDestinationValues = false:
public class A
{
static A()
{
Mapper.Initialize(
config =>
{
config.ForSourceType<B>().AllowNullDestinationValues = false;
config.CreateMap<B, A>()
.ForMember(member => member.Name, opt => opt.Ignore());
});
Mapper.Configuration.AllowNullDestinationValues = false;
Mapper.AssertConfigurationIsValid();
}
public void Init(B b)
{
Mapper.DynamicMap(b, this);
}
public int? Foo { get; set; }
public double? Foo1 { get; set; }
public bool Foo2 { get; set; }
public string Name { get; set; }
}

Categories

Resources