如何为每个AppDomain配置一次AutoMapper

我目前的项目包括域模型,MVC Web应用程序和unit testing的程序集。 如何设置AutoMapper配置,以便所有程序集引用相同的配置?

我猜我可以在Global.asax中为web应用程序添加项目,但是如何在unit testing中使用它? 此外,如果配置在Global.asax中,域模型是否会选择地图?

非常感谢,

KevDog。

我们要做的是创建一个静态类,比如BootStrapper,并将初始化代码放在静态方法中。 我们正在做个人资料,所以你在那里看不到多少。 Global.asax将在启动时调用它,域将使用它(因为配置是单例),并且需要它的unit testing在其设置中调用BootStrapper.Configure()。

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

HTH

我还使用引导程序来处理这种启动任务。 实际上,我使用了一系列bootstrappers,因为我很疯狂。 自动化,我们发现制作一些AutoMappingBuddy类并使用属性装饰它们更加清晰。 然后我们通过一些reflection调用连接映射器(不便宜,但它们只在开始时触发一次)。 在我们厌倦了在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 mappingDictionary = GetMappingDictionary(a); foreach (Type t in a.GetTypes()) { var amba = t.GetCustomAttributes(typeof (AutoMappingBuddyAttribute), true).OfType(). 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 GetMappingDictionary(Assembly a) { if (!assemblyMappings.ContainsKey(a)) { assemblyMappings.Add(a, new Dictionary()); } return assemblyMappings[a]; } private static Dictionary> assemblyMappings = new Dictionary>(); } 

有点打了一个小时左右,有可能更优雅的方式到达那里。

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

 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 autoMappingTypePairingList = new List(); foreach (Type t in a.GetTypes()) { var amba = t.GetCustomAttributes(typeof(AutoMappingAttribute), true).OfType().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); 

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

这是界面:

 public interface IAutoMapperRegistrar { void RegisterMaps(); } 

这是接口的实现:

 public class EventLogRowMaps : IAutoMapperRegistrar { public void RegisterMaps() { Mapper.CreateMap() .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(); } } 

我认为这是合适的,至少有点合乎逻辑; 他们更容易遵循这种方式。 在我用一个巨大的bootstrap方法进行了数百次注册之前,这开始变成了屁股的痛苦。

思考?