我的當前項目包含用於域模型,MVC Web應用程序和單元測試的程序集。我如何設置AutoMapper配置,以便所有程序集引用相同的配置?如何爲每個AppDomain配置一次AutoMapper
我想我可以把項目放在Global.asax中的web應用程序,但我怎麼能在單元測試中使用它呢?另外,如果配置位於Global.asax中,域模型是否會提取地圖?
非常感謝,
KevDog。
我的當前項目包含用於域模型,MVC Web應用程序和單元測試的程序集。我如何設置AutoMapper配置,以便所有程序集引用相同的配置?如何爲每個AppDomain配置一次AutoMapper
我想我可以把項目放在Global.asax中的web應用程序,但我怎麼能在單元測試中使用它呢?另外,如果配置位於Global.asax中,域模型是否會提取地圖?
非常感謝,
KevDog。
我們所做的是創建一個靜態類,像BootStrapper,並將初始化代碼放在一個靜態方法中。我們正在做個人資料,所以你在那裏看不到太多內容。 Global.asax將在啓動時調用它,域將使用它(因爲配置是單例),並且需要它的單元測試在它們的設置中調用BootStrapper.Configure()。
我們做的最後一件事情是在引導程序上保持一個標誌,並在配置時將其設置爲true。這樣,配置僅對每個AppDomain執行一次。這意味着一旦啓動global.asax(Application_Start),並且一次運行單元測試。
HTH
我也使用引導程序來處理這種啓動任務的東西。實際上,我使用了一系列bootstrappers,因爲我像那樣瘋狂。通過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>>();
}
有點在一個小時左右,有可能更優雅的方式到達那裏。
Wyatt,如果你有機會就你如何做到這一點做一個帖子,我肯定會閱讀它。這聽起來很優雅。 – KevDog 2009-09-30 14:25:06
對於大型配置文件,這可能也會添加到AutoMapper中。 v.next的好主意! – 2009-09-30 14:58:11
已發佈代碼。 @Jimmy:如果您願意,我可以提交補丁,讓我知道它應該放到代碼庫中的位置。 – 2009-09-30 15:37:50
我試過上面的代碼,但無法讓它工作。我修改了一下,如下所示。我認爲剩下要做的就是通過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<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);
我一直將我的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();
}
}
我認爲這是合適的,至少有點合乎邏輯;他們更容易遵循這種方式。在我用一種巨大的bootstrap方法進行了數百次註冊之前,這已經開始成爲屁股疼痛了。
想法?
謝謝吉米!在這個工具上工作出色,它在多個層面上都很有用。我喜歡bootstrapper上的國旗。今晚我會把這個插入我的代碼中。 – KevDog 2009-09-30 14:24:33
謝謝!這幫了我很多。至於這個工具,我非常喜歡它! – Rushino 2011-09-20 16:57:32