简体   繁体   English

AutoMapper,如何在映射对象之间保持引用?

[英]AutoMapper, how to keep references between mapped objects?

I am using AutoMapper to convert a UI model to POCOs that I later serialize to XML using a DataContractSerializer in order to preserve the references between them. 我正在使用AutoMapper将UI模型转换为POCO,之后我使用DataContractSerializer将其序列化为XML,以便保留它们之间的引用。

The problem comes that, when mapping, the references between those entities are lost . 问题是,在映射时, 这些实体之间的引用会丢失

The UI classes reference each other, but the mapping process makes new instances for every reference, so the original relations are broken :( UI类相互引用,但映射过程为每个引用创建新实例,因此原始关系被破坏:(

Let me explain: 让我解释:

I have 2 entities of type Person 我有2个Person类型的实体

    Person 
    { 
        List<House> OwnedHouses 
    }

And these 2 objects 而这2个对象

John who owns 约翰谁拥有

  • House1 House1

Will who also owns 请问谁也拥有

  • House1 House1

When AutoMapper maps each Person correctly, but when it also maps House1 as two different instances!! 当AutoMapper正确映射每个Person时,但它也将House1映射为两个不同的实例!!

So I have a two copies of House1. 所以我有两份House1。 John owns his House1 (#1) and Will owns his House1 (#2). John拥有他的House1(#1)并且Will拥有他的House1(#2)。

They are not linked anymore. 他们不再联系了。

Is there any way to keep the relations that originally existed? 有没有办法保持最初存在的关系?

Thanks. 谢谢。

EDITED: Actually what I have is this: 编辑:其实我的拥有是这样的:

A Document contains a list of ChildDocuments. 文档包含ChildDocuments列表。 Each ChildDocument has a list of Designables (Rectangles, Lines, Ellipses…) and a especial designable called ChildDocumentAdapter that contains itself ANOOTHER ChildDocument. 每个ChildDocument都有一个Designables列表(Rectangles,Lines,Ellipses ...)和一个特殊的可设计名为ChildDocumentAdapter,它包含自己的ANOOTHER ChildDocument。 This is the trouble, it can reference another ChildDocument. 这是麻烦,它可以引用另一个ChildDocument。

图

If I'm understanding the question, you're performing two separate mapping operations - one for John, another for Will. 如果我理解这个问题,你就会执行两个单独的映射操作 - 一个用于John,另一个用于Will。

@Sunny is right. @Sunny是对的。 AutoMapper is not designed to do this. AutoMapper不是为此而设计的。 Each call you make to Mapper.Map() is typically independent of any other. 您对Mapper.Map()每次调用通常都独立于其他任何调用。 By using the same instance of the HouseListConverter, you get the benefit of caching all mapped houses in a dictionary. 通过使用HouseListConverter的相同实例,您可以获得在字典中缓存所有映射的房屋的好处。 But you have to either register it globally or pass it as an option to the mapping calls you want grouped together. 但您必须全局注册它或将其作为选项传递给您想要组合在一起的映射调用。 That's not just extra work, it's hiding a very important implementation detail deep within the converter. 这不仅仅是额外的工作,它隐藏了转换器内部非常重要的实现细节。

If you map both John and Will in one operation, by putting them into a collection, the output would be what you want without the need for a custom converter or resolver. 如果您在一次操作中映射John和Will,通过将它们放入集合中,输出将是您想要的,而无需自定义转换器或解析器。

It may be an easier alternative for other people with a similar problem. 对于有类似问题的其他人来说,这可能是一个更容易的选择。

public void MapListOfPeopleWithSameHouse()
{
    Mapper.CreateMap<Person, PersonDTO>();
    Mapper.CreateMap<House, HouseDTO>();

    var people = new List<Person>();
    var house = new House() { Address = "123 Main" };
    people.Add(new Person() { Name = "John", Houses = new List<House>() { house } });
    people.Add(new Person() { Name = "Will", Houses = new List<House>() { house } });

    var peopleDTO = Mapper.Map<List<PersonDTO>>(people);
    Assert.IsNotNull(peopleDTO[0].Houses);
    Assert.AreSame(peopleDTO[0].Houses[0], peopleDTO[1].Houses[0]);
}

While Automapper is not designed with this in mind, it's powerful enough to let you do it, using custom type converters . 虽然Automapper的设计并未考虑到这一点,但它足够强大,可以让您使用自定义类型转换器 You need to create your own converter from IList<House> to IList<HouseDto> , and inject it using a factory: 您需要从IList<House>创建自己的转换器到IList<HouseDto> ,并使用工厂注入它:

using System;
using System.Collections.Generic;
using AutoMapper;
using NUnit.Framework;
using SharpTestsEx;

namespace StackOverflowExample
{
    public class House
    {
        public string Address { get; set; }
    }

    public class Person
    {
        public IList<House> OwnedHouse { get; set; }
    }

    public class HouseDto
    {
        public string Address { get; set; }
    }

    public class PersonDto
    {
        public IList<HouseDto> OwnedHouse { get; set; }
    }

    [TestFixture]
    public class AutomapperTest
    {
        public interface IHouseListConverter : ITypeConverter<IList<House>, IList<HouseDto>>
        {
        }

        public class HouseListConverter : IHouseListConverter
        {
            private readonly IDictionary<House, HouseDto> existingMappings;

            public HouseListConverter(IDictionary<House, HouseDto> existingMappings)
            {
                this.existingMappings = existingMappings;
            }

            public IList<HouseDto> Convert(ResolutionContext context)
            {
                var houses = context.SourceValue as IList<House>;
                if (houses == null)
                {
                    return null;
                }

                var dtos = new List<HouseDto>();
                foreach (var house in houses)
                {
                    HouseDto mapped = null;
                    if (existingMappings.ContainsKey(house))
                    {
                        mapped = existingMappings[house];
                    }
                    else
                    {
                        mapped = Mapper.Map<HouseDto>(house);
                        existingMappings[house] = mapped;
                    }
                    dtos.Add(mapped);
                }

                return dtos;
            }
        }

        public class ConverterFactory
        {
            private readonly IHouseListConverter resolver;
            public ConverterFactory()
            {
                resolver = new HouseListConverter(new Dictionary<House, HouseDto>());
            }

            public object Resolve(Type t)
            {
                return t == typeof(IHouseListConverter) ? resolver : null;
            }
        }

        [Test]
        public void CustomResolverTest()
        {
            Mapper.CreateMap<House, HouseDto>();
            Mapper.CreateMap<IList<House>, IList<HouseDto>>().ConvertUsing<IHouseListConverter>();
            Mapper.CreateMap<Person, PersonDto>();

            var house = new House {Address = "any"};
            var john = new Person {OwnedHouse = new List<House> {house}};
            var will = new Person { OwnedHouse = new List<House> { house } };

            var converterFactory = new ConverterFactory();
            var johnDto = Mapper.Map<PersonDto>(john, o=>o.ConstructServicesUsing(converterFactory.Resolve));
            var willDto = Mapper.Map<PersonDto>(will, o=>o.ConstructServicesUsing(converterFactory.Resolve));

            johnDto.OwnedHouse[0].Should().Be.SameInstanceAs(willDto.OwnedHouse[0]);
            johnDto.OwnedHouse[0].Address.Should().Be("any");
        }
    }
}  

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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