AutoMapper:使用“Ignore”在两个集合之间进行映射

3
我正在使用AutoMapper来映射这两个集合。但是我发现选项Ignore在这种情况下不像预期的那样工作。我期望的结果可以在方法AutoMapperIgnore_TwoObjectMappedWithIgnoreId_SameWithUnchangedIdAndNewPrice()中看到。在另外两个测试方法中,尽管Id被忽略了,但是集合中的每个对象都会再次创建,并且原始值将丢失。虽然还有可能使用UseDestinationValue,但我认为这只对集合是其成员的类有意义。我该如何在集合上使用Ignore选项?
[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; }
    }
}
2个回答

1

正如@Stef所提到的,您必须逐个映射集合中的每个条目,就像这样:

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

这是完整的功能示例:

[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; }
    }
}

0

我做了一些测试并确认了您的问题。

当映射一个IEnumerable列表时,AutoMapper会创建一个新的IEnumerable列表,其中仅包含您定义要映射的属性,因此只有“Price”。

要将Dto映射到实体,您需要定义一个使它们唯一的属性,例如主键。

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

将一个列表映射到另一个列表可以像这样完成:

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

或者更加 Linq 友好:

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

1
谢谢提供信息,但那似乎是一个bug,因为通常不需要为集合创建额外的映射! - rene_r
1
我会在 Github 上添加一个问题,看看 Jimmy 能否帮助我们。 - Stef Heyenrath
@Steff:谢谢。你能把问题的链接发到这里吗?顺便说一下,我找到了一个非常相似的问题(即使该问题与集合无关)。https://github.com/AutoMapper/AutoMapper/issues/319 - rene_r
@Stef 谢谢你的提示,但是当我为IEnumerables创建Map时,属性根本没有被映射。 - Anton Kalcik
@Stef 我已经设置好了两个。 - Anton Kalcik
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接