20

我当前的项目包含域模型、MVC Web 应用程序和单元测试的程序集。如何设置 AutoMapper 配置,以便所有程序集都引用相同的配置?

我想我可以将项目放入 Web 应用程序的 Global.asax 中,但是如何在单元测试中使用它呢?另外,如果配置在 Global.asax 中,域模型会选择地图吗?

非常感谢,

凯夫狗。

4

4 回答 4

28

我们要做的是创建一个静态类,例如 BootStrapper,并将初始化代码放入其中的静态方法中。我们正在制作个人资料,因此您在其中看不到太多内容。Global.asax 将在启动时调用它,域将使用它(因为配置是单例的),并且需要它的单元测试在其设置中调用 BootStrapper.Configure()。

我们要做的最后一件事是在引导程序上保留一个标志,并在我们配置时将其设置为 true。这样,每个 AppDomain 只执行一次配置。这意味着在 global.asax (Application_Start) 启动时一次,在我们运行单元测试时一次。

高温高压

于 2009-09-30T02:52:47.537 回答
4

我还使用引导程序来处理这种启动任务。实际上,我使用了一系列引导程序,因为我就是那样疯狂。Automapper 方面,我们发现创建一些 AutoMappingBuddy 类并用属性装饰它们要干净得多。然后我们通过一些反射调用连接映射器(不便宜,但它们只在开始时触发一次)。这个解决方案是在我们厌倦了在 1200+ 行文件的第 841 行中发现 AutoMapper 问题后发现的。


我考虑过发布代码,但我真的不能称之为purdy。无论如何,这里是:

首先,AutoMappingBuddies 的简单接口:

public interface IAutoMappingBuddy
{
    void CreateMaps();
}

其次,提供一些胶水的小属性:

public class AutoMappingBuddyAttribute : Attribute
{
    public Type MappingBuddy { get; private set; }

    public AutoMappingBuddyAttribute(Type mappingBuddyType)
    {
        if (mappingBuddyType == null) throw new ArgumentNullException("mappingBuddyType");
        MappingBuddy = mappingBuddyType;
    }

    public IAutoMappingBuddy CreateBuddy()
    {
        ConstructorInfo ci = MappingBuddy.GetConstructor(new Type[0]);
        if (ci == null)
        {
            throw new ArgumentOutOfRangeException("mappingBuddyType", string.Format("{0} does not have a parameterless constructor."));
        }
        object obj = ci.Invoke(new object[0]);
        return obj as IAutoMappingBuddy;
    }
}

第三,AutoMappingEngine。这就是魔法发生的地方:

public static class AutoMappingEngine
{
    public static void CreateMappings(Assembly a)
    {
        Dictionary<Type, IAutoMappingBuddy> mappingDictionary = GetMappingDictionary(a);
        foreach (Type t in a.GetTypes())
        {
            var amba =
                t.GetCustomAttributes(typeof (AutoMappingBuddyAttribute), true).OfType<AutoMappingBuddyAttribute>().
                    FirstOrDefault();
            if (amba!= null && !mappingDictionary.ContainsKey(amba.MappingBuddy))
            {
                mappingDictionary.Add(amba.MappingBuddy, amba.CreateBuddy());
            }
        }
        foreach (IAutoMappingBuddy mappingBuddy in mappingDictionary.Values)
        {
            mappingBuddy.CreateMaps();
        }
    }

    private static Dictionary<Type, IAutoMappingBuddy> GetMappingDictionary(Assembly a)
    {
        if (!assemblyMappings.ContainsKey(a))
        {
            assemblyMappings.Add(a, new Dictionary<Type, IAutoMappingBuddy>());
        }
        return assemblyMappings[a];
    }

    private static Dictionary<Assembly, Dictionary<Type, IAutoMappingBuddy>> assemblyMappings = new Dictionary<Assembly, Dictionary<Type, IAutoMappingBuddy>>();
}

差不多一个小时左右就打了起来,可能有更优雅的方法可以到达那里。

于 2009-09-30T13:39:17.720 回答
4

我尝试了上面的代码,但无法让它工作。我对其进行了一些修改,如下所示。我认为剩下要做的就是通过 Global.asax 的引导程序调用它。希望这可以帮助。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

using AutoMapper;

namespace Automapping
{
    public class AutoMappingTypePairing
    {
        public Type SourceType { get; set; }
        public Type DestinationType { get; set; }
    }

    public class AutoMappingAttribute : Attribute 
    {
        public Type SourceType { get; private set; }

        public AutoMappingAttribute(Type sourceType)
        {
            if (sourceType == null) throw new ArgumentNullException("sourceType");
            SourceType = sourceType; 
        }
    }

    public static class AutoMappingEngine
    {
        public static void CreateMappings(Assembly a)
        {
            IList<AutoMappingTypePairing> autoMappingTypePairingList = new List<AutoMappingTypePairing>();

            foreach (Type t in a.GetTypes())
            {
                var amba = t.GetCustomAttributes(typeof(AutoMappingAttribute), true).OfType<AutoMappingAttribute>().FirstOrDefault();

                if (amba != null)
                {
                    autoMappingTypePairingList.Add(new AutoMappingTypePairing{ SourceType = amba.SourceType, DestinationType = t});
                }
            } 

            foreach (AutoMappingTypePairing mappingPair in autoMappingTypePairingList) 
            {
                Mapper.CreateMap(mappingPair.SourceType, mappingPair.DestinationType);
            }
        }
    }
}

我像这样使用它来将源与目标配对相关联:

[AutoMapping(typeof(Cms_Schema))]
public class Schema : ISchema
{
    public Int32 SchemaId { get; set; }
    public String SchemaName { get; set; }
    public Guid ApplicationId { get; set; }
}

然后自动创建映射,我这样做:

        Assembly assembly = Assembly.GetAssembly(typeof([ENTER NAME OF A TYPE FROM YOUR ASSEMBLY HERE]));

        AutoMappingEngine.CreateMappings(assembly);
于 2009-11-06T22:07:14.063 回答
2

我一直在将我的 AutoMapper CreateMap 调用移动到位于我的视图模型旁边的类中。他们实现了一个 IAutomapperRegistrar 接口。我使用反射来查找 IAutoMapperRegistrar 实现,创建一个实例并添加注册。

这是界面:

public interface IAutoMapperRegistrar
{
    void RegisterMaps();
}

这是接口的实现:

public class EventLogRowMaps : IAutoMapperRegistrar
{
    public void RegisterMaps()
    {
        Mapper.CreateMap<HistoryEntry, EventLogRow>()
            .ConstructUsing(he => new EventLogRow(he.Id))
            .ForMember(m => m.EventName, o => o.MapFrom(e => e.Description))
            .ForMember(m => m.UserName, o => o.MapFrom(e => e.ExecutedBy.Username))
            .ForMember(m => m.DateExecuted, o => o.MapFrom(e => string.Format("{0}", e.DateExecuted.ToShortDateString())));
    }
}

这是在我的 Application_Start 中执行注册的代码:

foreach (Type foundType in Assembly.GetAssembly(typeof(ISaveableModel)).GetTypes())
{
    if(foundType.GetInterfaces().Any(i => i == typeof(IAutoMapperRegistrar)))
    {
        var constructor = foundType.GetConstructor(Type.EmptyTypes);
        if (constructor == null) throw new ArgumentException("We assume all IAutoMapperRegistrar classes have empty constructors.");
        ((IAutoMapperRegistrar)constructor.Invoke(null)).RegisterMaps();
    }
}

我认为这是合适的,至少有点合乎逻辑;他们更容易遵循这种方式。在我以一个巨大的引导方法进行数百次注册之前,这开始变得令人头疼。

想法?

于 2012-10-26T18:15:43.507 回答