我可以帮您翻译成中文:我可以禁用AutoMapper的引用缓存吗?

10

我相信AutoMapper在将一个类型的对象列表映射到另一种类型时会使用缓存。看一下以下代码:

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            Mapper.CreateMap<Source, Destination>()
                       .ForMember(dest => dest.SomeOtherNumber, opt => opt.Ignore());

            Mapper.AssertConfigurationIsValid();

            var sourceList = new List<Source>();

            var s1 = new Source {SomeNumber = 10};
            var s2 = new Source {SomeNumber = 69};

            sourceList.Add(s1);
            sourceList.Add(s1);
            sourceList.Add(s1);
            sourceList.Add(s2);

            var destList = Mapper.Map<List<Source>, List<Destination>>(sourceList);

            destList[0].SomeOtherNumber = 100;

            destList.ForEach(x => Console.WriteLine("SomeNumber: {0}, SomeOtherNumber: {1}", x.SomeNumber, x.SomeOtherNumber));
            Console.ReadLine();
        }
    }


    public class Source
    {
        public int SomeNumber { get; set; }
    }

    public class Destination
    {
        public int SomeNumber { get; set; }
        public int SomeOtherNumber { get; set; }
    }

}

代码的输出显示,即使我只为destList列表中的第一个对象设置了SomeOtherNumber,前三个项仍具有相同的属性值,因为它们引用了同一对象引用。 我想知道是否可以禁用此行为,因此即使源列表中存在重复引用,目标列表中也不会有重复引用。这有意义吗?

下面是输出:

SomeNumber: 10, SomeOtherNumber: 100
SomeNumber: 10, SomeOtherNumber: 100
SomeNumber: 10, SomeOtherNumber: 100
SomeNumber: 69, SomeOtherNumber: 0

1
AutoMapper是一个不错的工具,但是我们已经转而使用Value Injecter。在我看来,它更容易使用,没有配置/设置,并且解决了您在AutoMapper OOB中看到的引用共享问题。 - Metro Smurf
我很感激您的评论。当我在尝试从其他SO问题中弄清楚这个问题时,我确实看到了Value Injecter的提及。如果我无法解决这个问题,我可能最终会使用它。 - BlakeH
如果你将Destination从类更改为结构体,它会正常工作。这样做的缺点是什么? - BuddyJoe
1个回答

5

我认为目前的AutoMapper无法做到这一点。实例缓存被深度内置,目前很难关闭。虽然你的用例非常奇怪……在列表中多次使用相同的源对象,但仍然希望有不同的目标对象,在我看来相当不寻常。

但是可以通过一个相当糟糕的hack来解决它,因为它会全局关闭实例缓存,并依赖于AutoMapper的实现细节,所以请谨慎使用。如果您仍然想尝试它,下面提到的解决方案不能与Automapper 2.2.0一起使用因为有一个bug,只有在开发分支中才得到修复,因此您需要从源代码构建AutoMapper或等待下一个版本。

但是,以下是详细信息:

实例缓存由TypeMapObjectMapperRegistry.CacheMappingStrategy[来源]处理,就像所有其他ITypeMapObjectMapper实现一样,它是一个私有类。

然而,TypeMapObjectMapperRegistry.AllMappers字段应该返回所有ITypeMapObjectMapper,它是一个公共的Func,可以被覆盖:

因此,在第一次使用Mapper类之前添加以下代码:

var baseMappers = TypeMapObjectMapperRegistry.AllMappers();
TypeMapObjectMapperRegistry.AllMappers = () => 
    baseMappers.Where(m => m.GetType().Name != "CacheMappingStrategy").ToArray();

Mapper.CreateMap<Source, Destination>();

好的,我感谢这些信息。我同意使用案例可能看起来很奇怪,但我觉得认为默认行为总是正确有点傲慢。 - BlakeH
挺有趣的一个bug :) 已经过去将近两年了,问题仍然存在。现在好问题是禁用缓存策略会有什么后果。 - George Onofrei
Mapper.Reset() 有帮助吗? - jwize
Mapper.Reset()可以帮助,但它也会重置所有映射,因此您必须再次调用所有的 Mapper.CreateMaps。所以最好手动删除 CacheMappingStrategy - nemesv

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