2009-09-29 2 views
19

도메인 모델, MVC 웹 응용 프로그램 및 단위 테스트 용 어셈블리가있는 현재 프로젝트입니다. 모든 어셈블리가 동일한 구성을 참조하도록 AutoMapper 구성을 어떻게 설정할 수 있습니까?AppModel 당 AppMomer를 한 번 구성하는 방법

웹 응용 프로그램의 Global.asax에 항목을 넣을 수는 있지만 단위 테스트에서는 어떻게 사용할 수 있습니까? 또한 config가 Global.asax에 있으면 도메인 모델이지도를 선택합니까?

많은 감사,

KevDog.

답변

28

우리가하는 일은 BootStrapper와 같은 정적 클래스를 만들고 거기에 정적 메서드로 초기화 코드를 넣는 것입니다. 우리는 프로필을하고있어, 당신은 그곳에별로 보이지 않습니다. Global.asax는 시작할 때 도메인이 그것을 사용할 것이고 (구성은 싱글 톤이기 때문에), 유닛 테스트는 자신의 설정에서 BootStrapper.Configure()를 호출해야합니다.

마지막으로 부트 스트 래퍼에 플래그를 지정하고 구성 할 때 true로 설정하십시오. 이렇게하면 구성은 AppDomain 당 한 번만 실행됩니다. 이는 global.asax (Application_Start)를 시작할 때 한 번, 그리고 단위 테스트를 실행할 때 한 번 의미합니다.

HTH는

+1

감사합니다. 지미! 이 도구는 탁월한 작업으로 다양한 수준에서 작동합니다. 부트 스트 래퍼의 깃발이 마음에 듭니다. 오늘 밤 내 코드에 이걸 집어 넣을거야. – KevDog

+0

감사! 이것은 제게 많은 도움이되었습니다. 이 도구는 매우 강력합니다. – Rushino

4

또한 시작 작업의 이런 종류의 물건을 처리하기 위해 부트 스트 래퍼를 사용합니다. 사실, 나는 그처럼 미친 때문에 부트 스트 래퍼 체인을 사용합니다. Automapper-wise, 우리는 AutoMappingBuddy 클래스를 만들고 속성으로 꾸미는 것이 더 깔끔하다는 것을 발견했습니다. 그런 다음 반사 전화를 통해 매퍼를 연결합니다 (저렴하지는 않지만 한 번만 발사합니다). 이 해결책은 우리가 1200 + 라인 파일의 841 행에 AutoMapper 문제를 찾는 것을 아프게 한 후에 발견되었습니다.


나는 코드를 게시하는 것에 대해 생각했지만 실제로는 그렇게 할 수는 없다. 어쨌든, 여기 간다 :

첫째, AutoMappingBuddies에 대한 간단한 인터페이스 :

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 interface IAutoMappingBuddy 
{ 
    void CreateMaps(); 
} 

둘째, 약간의 속성을 일부 접착제를 제공 할 수 있습니다. 마법의 발생 장소는 다음과 같습니다.

친절한 사람들이 1 시간 내에 한꺼번에 때려 눕 혔을 가능성이 있습니다.

+0

와이어트, 당신이 어떻게했는지에 대한 글을 올릴 기회가 있다면, 필자는 그것을 읽으려고 줄줄이 분명히있을 것입니다. 그것은 우아한 소리. – KevDog

+0

큰 구성 파일의 경우 AutoMapper에 추가 할 수 있습니다. v.next에 대한 좋은 아이디어! –

+0

코드가 게시되었습니다. @ 지미 : 원하는 경우 패치를 제출할 수 있습니다. 코드베이스에서 어디로 가야할지 알려주세요. –

4

위 코드를 시도했지만 작동하지 못했습니다. 나는 그것을 다음과 같이 약간 수정했다. 나는 할 일이 남아있는 것은 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); 
2

을 내 AutoMapper CreateMap 호출을 내 뷰 모델 옆에있는 클래스로 이동합니다. IAutomapperRegistrar 인터페이스를 구현합니다. 리플렉션을 사용하여 IAutoMapperRegistrar 구현을 찾고, 인스턴스를 만들고 등록을 추가합니다.

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(); 
    } 
} 

: 여기

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에서 등록을 수행하는 코드이다 : 여기
public interface IAutoMapperRegistrar 
{ 
    void RegisterMaps(); 
} 

인터페이스의 구현입니다 : 여기

인터페이스입니다 나는 그것이 적절하고 적어도 조금 논리적 인 것으로 생각한다; 그 방법을 따르는 것이 훨씬 쉽습니다. 한 번의 거대한 부트 스트랩 방법으로 수백 건의 등록을하기 전에 엉덩이에 통증이 생기기 시작했습니다.

생각하십니까?

관련 문제