简体   繁体   中英

AutoMapper: Mapping between the two collections with 'Ignore'

I'm using AutoMapper to map between the two collections. What I see is that the option Ignore is not working at this scenario as expected. What i'm expecting can be see in the method AutoMapperIgnore_TwoObjectMappedWithIgnoreId_SameWithUnchangedIdAndNewPrice() . In other two test methods is the Id although ignored but every object in the collection is created once again with the consequences that original values are going to be lost. There is possibility to use UseDestinationValue , but I think this make only sense on the classes where collection are members of it. How can I use the option Ignore on the collections?

[TestClass]
public class AutoMapperTests
{
    private readonly IEnumerable<Dto> _testDtos;

    private readonly IEnumerable<Entity> _testEntities;

    public AutoMapperTests()
    {
        _testDtos = new List<Dto>
        {
            new Dto()
            {
                Id = 0,
                Fk_Id = 8,
                Price = 350000
            }
        };


        _testEntities = new List<Entity>
        {
            new Entity()
            {
                Id = 8,
                Price = 68000
            }
            ,
            new Entity()
            {
                Id = 6,
                Price = 350000
            }
        };
    }

    [TestInitialize]
    public void TestInitialize()
    {
        Mapper.Reset();
    }

    [TestMethod]
    public void AutoMapperIgnore_TwoCollectionsWithOneCommonElementMappedFromDtoToEntityIgnoreId_SameWithUnchangedIdAndNewPrice()
    {
        //Assign
        Mapper.CreateMap<Dto, Entity>()
            .ForMember(destination => destination.Id, opt => opt.Ignore());

        AutoMapperIgnore_TwoCollectionsWithOneCommonElementMappedFromDtoToEntity_SameWithUnchangedIdAndNewPrice(true);
    }

    [TestMethod]
    public void AutoMapperIgnore_TwoCollectionsWithOneCommonElementMappedFromDtoToEntityUseDestinationtValueForId_SameWithUnchangedIdAndNewPrice()
    {
        //Assign
        Mapper.CreateMap<Dto, Entity>()
            .ForMember(destination => destination.Id, opt => opt.UseDestinationValue());

        AutoMapperIgnore_TwoCollectionsWithOneCommonElementMappedFromDtoToEntity_SameWithUnchangedIdAndNewPrice(true);
    }


    private void AutoMapperIgnore_TwoCollectionsWithOneCommonElementMappedFromDtoToEntity_SameWithUnchangedIdAndNewPrice(bool isExceptedSame)
    {
        //Assign
        var exceptedPrice = _testDtos.First().Price;

        //Act
        IEnumerable<Entity> foundEntities = _testEntities.Join(_testDtos, e => e.Id, e => e.Fk_Id, (entity, dto) => entity).ToList();
        Entity entityBeforeMapping = foundEntities.First();

        Mapper.Map(_testDtos, foundEntities);

        Entity entityAfterMapping = foundEntities.First();


        //Assert
        if (isExceptedSame)
        {
            Assert.AreSame(entityBeforeMapping, entityAfterMapping);
        }
        Assert.AreEqual(entityBeforeMapping.Id, entityAfterMapping.Id);
        Assert.AreEqual(exceptedPrice, entityAfterMapping.Price);
    }

    [TestMethod]
    public void AutoMapperIgnore_TwoObjectMappedWithIgnoreId_SameWithUnchangedIdAndNewPrice()
    {
        //Assign
        Mapper.CreateMap<Dto, Entity>()
            .ForMember(destination => destination.Id, opt => opt.Ignore());

        var testDto = new Dto()
        {
            Id = 0,
            Fk_Id = 8,
            Price = 350000
        };

        var testEntity = new Entity()
        {
            Id = 8,
            Price = 68000
        };

        var exceptedPrice = testDto.Price;


        //Act
        Entity entityBeforeMapping = testEntity;
        Mapper.Map(testDto, testEntity);
        Entity entityAfterMapping = testEntity;


        //Assert
        Assert.AreSame(entityBeforeMapping, entityAfterMapping);
        Assert.AreEqual(entityBeforeMapping.Id, entityAfterMapping.Id);
        Assert.AreEqual(exceptedPrice, entityAfterMapping.Price);
    }

    internal class Dto
    {
        public int Id { get; set; }
        public int Fk_Id { get; set; }
        public Single? Price { get; set; }
    }

    internal class Entity
    {
        public int Id { get; set; }
        public Single? Price { get; set; }
    }
}

As @Stef mentioned you must map each entry in the collection individually, like this

_testEntities.Join(_testDtos, e => e.Id, e => e.Fk_Id, (entity, dto) => Mapper.Map(dto,entity));

Here the full functionally example:

[TestClass]
public class AutoMapperTests
{
    private readonly IEnumerable<Dto> _testDtos;

    private readonly IEnumerable<Entity> _testEntities;

    public AutoMapperTests()
    {
        _testDtos = new List<Dto>
        {
            new Dto()
            {
                Id = 0,
                Fk_Id = 8,
                Price = 350000
            }
        };


        _testEntities = new List<Entity>
        {
            new Entity()
            {
                Id = 8,
                Price = 68000
            }
            ,
            new Entity()
            {
                Id = 6,
                Price = 350000
            }
        };
    }

    [TestInitialize]
    public void TestInitialize()
    {
        Mapper.Reset();
    }

    [TestMethod]
    public void AutoMapperIgnore_TwoCollectionsWithOneCommonElementMappedFromDtoToEntityIgnoreId_SameWithUnchangedIdAndNewPrice()
    {
        //Assign
        Mapper.CreateMap<Dto, Entity>()
            .ForMember(destination => destination.Id, opt => opt.Ignore());

        AutoMapperIgnore_TwoCollectionsWithOneCommonElementMappedFromDtoToEntity_SameWithUnchangedIdAndNewPrice(true);
    }

    [TestMethod]
    public void AutoMapperIgnore_TwoCollectionsWithOneCommonElementMappedFromDtoToEntityUseDestinationtValueForId_SameWithUnchangedIdAndNewPrice()
    {
        //Assign
        Mapper.CreateMap<Dto, Entity>()
            .ForMember(destination => destination.Id, opt => opt.UseDestinationValue());


        AutoMapperIgnore_TwoCollectionsWithOneCommonElementMappedFromDtoToEntity_SameWithUnchangedIdAndNewPrice(true);
    }

    private void AutoMapperIgnore_TwoCollectionsWithOneCommonElementMappedFromDtoToEntity_SameWithUnchangedIdAndNewPrice(bool isExceptedSame)
    {
        //Assign
        var exceptedPrice = _testDtos.First().Price;

        //Act
        Entity entityBeforeMapping = _testEntities.First(testEntity => testEntity.Id == _testEntities.First().Id);
        IEnumerable<Entity> foundEntities = _testEntities.Join(_testDtos, e => e.Id, e => e.Fk_Id, (entity, dto) => Mapper.Map(dto,entity));
        Entity entityAfterMapping = foundEntities.First();


        //Assert
        if (isExceptedSame)
        {
            Assert.AreSame(entityBeforeMapping, entityAfterMapping);
        }
        Assert.AreEqual(entityBeforeMapping.Id, entityAfterMapping.Id);
        Assert.AreEqual(exceptedPrice, entityAfterMapping.Price);
    }

    [TestMethod]
    public void AutoMapperIgnore_TwoObjectMappedWithIgnoreId_SameWithUnchangedIdAndNewPrice()
    {
        //Assign
        Mapper.CreateMap<Dto, Entity>()
            .ForMember(destination => destination.Id, opt => opt.Ignore());

        var testDto = new Dto()
        {
            Id = 0,
            Fk_Id = 8,
            Price = 350000
        };

        var testEntity = new Entity()
        {
            Id = 8,
            Price = 68000
        };

        var exceptedPrice = testDto.Price;


        //Act
        Entity entityBeforeMapping = testEntity;
        Mapper.Map(testDto, testEntity);
        Entity entityAfterMapping = testEntity;


        //Assert
        Assert.AreSame(entityBeforeMapping, entityAfterMapping);
        Assert.AreEqual(entityBeforeMapping.Id, entityAfterMapping.Id);
        Assert.AreEqual(exceptedPrice, entityAfterMapping.Price);
    }

    internal class Dto
    {
        public int Id { get; set; }
        public int Fk_Id { get; set; }
        public Single? Price { get; set; }
    }

    internal class Entity
    {
        public int Id { get; set; }
        public Single? Price { get; set; }
    }
}

I did some tests and confirmed your issue.

When a IEnumerable list is mapped, AutoMapper creates a new IEnumerable list which only includes the properties you define to map, so only the 'Price'.

In order to map a Dto to an Entity, you need to define a property which makes them unique, like a PrimaryKey.

class Dto
{
    public long PrimaryKey { get; set; }
    public int Id { get; set; }
    public int Fk_Id { get; set; }
    public Single? Price { get; set; }
}

class Entity
{
    public long PrimaryKey { get; set; }
    public int Id { get; set; }

    public Single? Price { get; set; }
}

Mapping a List to a List can be done like this:

// Update entities in original list
foreach (var d in _testDtos)
{
    foreach (var e in _testEntities)
    {
        if (e.PrimaryKey == d.PrimaryKey)
        {
            Mapper.Map(d, e);
        }
    }
}

Or more linq-friendly:

// Create a new list
var foundEntities = _testEntities.Join(_testDtos, e => e.PrimaryKey, d => d.PrimaryKey, (entity, dto) => Mapper.Map<Entity>(dto)).ToList();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM