AutoMapper.CreateMaps कहां रखें?


216

मैं AutoMapperएक ASP.NET MVCअनुप्रयोग में उपयोग कर रहा हूँ । मुझे बताया गया था कि मुझे AutoMapper.CreateMapकहीं और जाना चाहिए क्योंकि उनके पास बहुत अधिक ओवरहेड है। मुझे यकीन नहीं है कि इन कॉल्स को केवल 1 स्थान पर रखने के लिए मेरे एप्लिकेशन को कैसे डिज़ाइन किया जाए।

मेरे पास एक वेब लेयर, सर्विस लेयर और एक डेटा लेयर है। प्रत्येक की अपनी एक परियोजना। मैं Ninjectसब कुछ DI का उपयोग करता हूं । मैं AutoMapperवेब और सेवा दोनों परतों में उपयोग करूँगा ।

तो क्या AutoMapperCreateMap के लिए आपका सेटअप क्या है ? आप इसे कहाँ लगाते हैं? आप इसे कैसे कहते हैं?

जवाबों:


219

कोई फर्क नहीं पड़ता, जब तक यह एक स्थिर वर्ग है। यह सब सम्मेलन के बारे में है

हमारा कन्वेंशन यह है कि प्रत्येक "लेयर" (वेब, सर्विसेज, डेटा) में एक सिंगल फाइल है AutoMapperXConfiguration.cs, जिसमें सिंगल मेथड है Configure(), जहां Xलेयर है।

Configure()विधि तो कॉल privateप्रत्येक क्षेत्र के लिए तरीके।

यहाँ हमारे वेब स्तरीय विन्यास का एक उदाहरण दिया गया है:

public static class AutoMapperWebConfiguration
{
   public static void Configure()
   {
      ConfigureUserMapping();
      ConfigurePostMapping();
   }

   private static void ConfigureUserMapping()
   {
      Mapper.CreateMap<User,UserViewModel>();
   } 

   // ... etc
}

हम प्रत्येक "एग्रीगेट" (उपयोगकर्ता, पोस्ट) के लिए एक विधि बनाते हैं, इसलिए चीजें अच्छी तरह से अलग हो जाती हैं।

फिर आपका Global.asax:

AutoMapperWebConfiguration.Configure();
AutoMapperServicesConfiguration.Configure();
AutoMapperDomainConfiguration.Configure();
// etc

यह एक "शब्दों के इंटरफ़ेस" की तरह है - इसे लागू नहीं कर सकता है, लेकिन आप इसकी उम्मीद करते हैं, इसलिए यदि आवश्यक हो तो आप कोड कर सकते हैं (और रिफ्लेक्टर कर सकते हैं।

संपादित करें:

बस मैंने सोचा था कि मैं अब AutoMapper प्रोफाइल का उपयोग करूँगा , इसलिए उपरोक्त उदाहरण बन जाता है:

public static class AutoMapperWebConfiguration
{
   public static void Configure()
   {
      Mapper.Initialize(cfg =>
      {
        cfg.AddProfile(new UserProfile());
        cfg.AddProfile(new PostProfile());
      });
   }
}

public class UserProfile : Profile
{
    protected override void Configure()
    {
         Mapper.CreateMap<User,UserViewModel>();
    }
}

बहुत क्लीनर / अधिक मजबूत।


2
@ AliRızaAdıyahşi दोनों प्रोजेक्ट में एक मैपिंग फ़ाइल होनी चाहिए। कोर में AutoMapperCoreConfiguration होना चाहिए, और UI में AutoMapperWebConfiguration होना चाहिए। वेब कॉन्फ़िगरेशन को कोर कॉन्फ़िगरेशन से प्रोफाइल को जोड़ना चाहिए।
RPM1984

7
क्या Mapper.Initializeप्रत्येक कॉन्फ़िगरेशन क्लास में कॉलिंग पिछले प्रोफाइल को अधिलेखित करती है? यदि हां, तो प्रारंभ के बजाय क्या उपयोग किया जाना चाहिए?
कोडी

4
क्या यह आपके वेब API प्रोजेक्ट को आपकी सेवा और डोमेन परतों का संदर्भ नहीं देता है?
18

3
अगर मेरे पास Web -> Service -> BLL -> DAL है। मेरी संस्थाएँ मेरे डीएएल में हैं। मैं अपने DAL का संदर्भ वेब या सेवा से नहीं देना चाहता। मैं इसे कैसे शुरू करूं?
व्याचे

19
AutoMapper के रूप में 4.2 Mapper.CreateMap()अब मोटे तौर पर है। 'Mapper.Map<TSource, TDestination>(TSource, TDestination)' is obsolete: 'The static API will be removed in version 5.0. Use a MapperConfiguration instance and store statically as needed. Use CreateMapper to create a mapper instance.'। नई आवश्यकताओं के अनुरूप आप अपने उदाहरण को कैसे अपडेट करेंगे?
। ᴍᴀᴛᴛ

34

आप वास्तव में इसे तब तक कहीं भी रख सकते हैं जब तक आपकी वेब परियोजना उस विधानसभा का संदर्भ देती है, जो आपकी स्थिति में है। आपकी स्थिति में मैं इसे सेवा स्तर पर रखूंगा क्योंकि यह वेब लेयर और सर्विस लेयर द्वारा सुलभ होगा और बाद में यदि आप निर्णय लेते हैं एक कंसोल ऐप करें या आप एक यूनिट टेस्ट प्रोजेक्ट कर रहे हैं, मैपिंग कॉन्फ़िगरेशन उन परियोजनाओं से भी उपलब्ध होगा।

अपने Global.asax में आप उस विधि को कहेंगे जो आपके सभी मानचित्र सेट करती है। निचे देखो:

फ़ाइल AutoMapperBootStrapper.cs

public static class AutoMapperBootStrapper
{
     public static void BootStrap()
     {  
         AutoMapper.CreateMap<Object1, Object2>();
         // So on...


     }
}

आवेदन शुरू करने पर Global.asax

बस कॉल करना

AutoMapperBootStrapper.BootStrap();

अब कुछ लोग इस पद्धति के खिलाफ बहस करेंगे जो कुछ ठोस सिद्धांतों का उल्लंघन करते हैं, जिनके पास वैध तर्क हैं। यहां वे पढ़ने के लिए हैं।

बूटस्ट्रैप में ऑटोमैपर को कॉन्फ़िगर करना ओपन-क्लोज्ड सिद्धांत का उल्लंघन करता है?


13
यह। उचित "कट्टर" वास्तुकला की ओर हर कदम तेजी से अधिक कोड को शामिल करता है। यह आसान है; यह 99.9% कोडर्स के लिए पर्याप्त होगा; और आपके सहकर्मी सादगी की सराहना करेंगे। हां, सभी को ओपन-क्लोज्ड सिद्धांत के बारे में मुद्दा पढ़ना चाहिए, लेकिन सभी को ट्रेडऑफ के बारे में भी सोचना चाहिए।
अनॉन

आपने AutoMapperBootStrapper वर्ग कहाँ बनाया है?
user6395764

16

अपडेट: यहां पोस्ट किया गया दृष्टिकोण और अधिक मान्य नहीं हैSelfProfiler है, क्योंकि AutoMapper v2 के रूप में हटा दिया गया है।

मैं थोई के समान दृष्टिकोण लेगा। लेकिन मैं SelfProfiler<>नक्शे को संभालने के लिए अंतर्निहित कक्षा का उपयोग करूंगा , फिर Mapper.SelfConfigureफ़ंक्शन को इनिशियलाइज़ करने के लिए उपयोग करूंगा ।

स्रोत के रूप में इस वस्तु का उपयोग करना:

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
    public string GetFullName()
    {
        return string.Format("{0} {1}", FirstName, LastName);
    }
}

और ये गंतव्य के रूप में:

public class UserViewModel
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class UserWithAgeViewModel
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public int Age { get; set; }
}

आप ये प्रोफाइल बना सकते हैं:

public class UserViewModelProfile : SelfProfiler<User,UserViewModel>
{
    protected override void DescribeConfiguration(IMappingExpression<User, UserViewModel> map)
    {
    //This maps by convention, so no configuration needed
    }
}

public class UserWithAgeViewModelProfile : SelfProfiler<User, UserWithAgeViewModel>
{
    protected override void DescribeConfiguration(IMappingExpression<User, UserWithAgeViewModel> map)
    {
    //This map needs a little configuration
        map.ForMember(d => d.Age, o => o.MapFrom(s => DateTime.Now.Year - s.BirthDate.Year));
    }
}

अपने आवेदन में आरंभ करने के लिए, यह वर्ग बनाएं

 public class AutoMapperConfiguration
 {
      public static void Initialize()
      {
          Mapper.Initialize(x=>
          {
              x.SelfConfigure(typeof (UserViewModel).Assembly);
              // add assemblies as necessary
          });
      }
 }

इस लाइन को अपने global.asax.cs फ़ाइल में जोड़ें: AutoMapperConfiguration.Initialize()

अब आप अपनी मैपिंग क्लासेस लगा सकते हैं, जहाँ वे आपसे कोई मतलब नहीं रखते हैं और एक मोनोलिथिक मैपिंग क्लास की चिंता नहीं करते हैं।


3
सिर्फ FYI करें, सेल्फप्रोफाइलर क्लास ऑटोमैपर v2 के बाद से चली गई है।
मैट हनीकट

15

आपमें से जो निम्नलिखित का पालन करते हैं:

  1. एक ioc कंटेनर का उपयोग कर
  2. इसके लिए खुले बंद को तोड़ना पसंद नहीं है
  3. एक अखंड विन्यास फ़ाइल पसंद नहीं है

मैंने प्रोफाइल और मेरे आईओक कंटेनर का लाभ उठाने के बीच एक कॉम्बो किया:

IoC कॉन्फ़िगरेशन:

public class Automapper : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly().BasedOn<Profile>().WithServiceBase());

        container.Register(Component.For<IMappingEngine>().UsingFactoryMethod(k =>
        {
            Profile[] profiles = k.ResolveAll<Profile>();

            Mapper.Initialize(cfg =>
            {
                foreach (var profile in profiles)
                {
                    cfg.AddProfile(profile);
                }
            });

            profiles.ForEach(k.ReleaseComponent);

            return Mapper.Engine;
        }));
    }
}

कॉन्फ़िगरेशन उदाहरण:

public class TagStatusViewModelMappings : Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<Service.Contracts.TagStatusViewModel, TagStatusViewModel>();
    }
}

उपयोग उदाहरण:

public class TagStatusController : ApiController
{
    private readonly IFooService _service;
    private readonly IMappingEngine _mapper;

    public TagStatusController(IFooService service, IMappingEngine mapper)
    {
        _service = service;
        _mapper = mapper;
    }

    [Route("")]
    public HttpResponseMessage Get()
    {
        var response = _service.GetTagStatus();

        return Request.CreateResponse(HttpStatusCode.Accepted, _mapper.Map<List<ViewModels.TagStatusViewModel>>(response)); 
    }
}

व्यापार-बंद यह है कि आपको स्थैतिक मैपर के बजाय IMpingEngine इंटरफ़ेस द्वारा मैपर को संदर्भित करना होगा, लेकिन यह एक ऐसा सम्मेलन है जिसके साथ मैं रह सकता हूं।


14

उपरोक्त सभी समाधान कॉल करने के लिए एक स्थिर विधि प्रदान करते हैं (app_start या किसी भी जहाँ से) कि यह मैपिंग-कॉन्फ़िगरेशन के कुछ हिस्सों को कॉन्फ़िगर करने के लिए अन्य तरीकों को कॉल करे। लेकिन, यदि आपके पास एक मॉड्यूलर अनुप्रयोग है, तो वह मॉड्यूल किसी भी समय एप्लिकेशन के अंदर और बाहर प्लग कर सकता है, ये समाधान काम नहीं करते हैं। मैं WebActivatorपुस्तकालय का उपयोग करने का सुझाव देता हूं जो कुछ तरीकों को चलाने के लिए पंजीकृत कर सकता है app_pre_startऔर app_post_startजहां भी:

// in MyModule1.dll
public class InitMapInModule1 {
    static void Init() {
        Mapper.CreateMap<User, UserViewModel>();
        // other stuffs
    }
}
[assembly: PreApplicationStartMethod(typeof(InitMapInModule1), "Init")]

// in MyModule2.dll
public class InitMapInModule2 {
    static void Init() {
        Mapper.CreateMap<Blog, BlogViewModel>();
        // other stuffs
    }
}
[assembly: PreApplicationStartMethod(typeof(InitMapInModule2), "Init")]

// in MyModule3.dll
public class InitMapInModule3 {
    static void Init() {
        Mapper.CreateMap<Comment, CommentViewModel>();
        // other stuffs
    }
}
[assembly: PreApplicationStartMethod(typeof(InitMapInModule2), "Init")]

// and in other libraries...

आप WebActivatorNuGet के माध्यम से स्थापित कर सकते हैं ।


2
मैं हाल ही में उसी निष्कर्ष पर आया हूं। यह आपके मानचित्र निर्माण कोड को उपभोग करने वाले कोड के करीब रखता है। यह विधि एक एमवीसी नियंत्रक को और अधिक बनाए रखने योग्य बनाती है।
mfras3r

मैं इसे कहीं भी कैसे शुरू कर सकता हूं, क्या आप एक उदाहरण प्रदान कर सकते हैं? आपका ब्लॉग लिंक काम नहीं करता है ...
व्यतीत

1
@ यह बहुत स्पष्ट है! में MyModule1परियोजना (या जो भी अपनी परियोजना का नाम है) सिर्फ एक वर्ग नामित बनाने InitMapInModule1और फ़ाइल के अंदर कोड डाल; अन्य मॉड्यूल के लिए भी ऐसा ही करें।
रावी अमीर

गोत्चा, मैंने वास्तव में बस कोशिश की। मैंने Nuget से WebActivator को अपनी कक्षा की लाइब्रेरी (DAL) में जोड़ा और एक स्थैतिक AutoMapperDalConfiguration क्लास बनाया जिसमें मैंने मैप्स को कॉन्फ़िगर करने और प्रारंभ करने के लिए @ RPM1984 कार्यान्वयन बनाया। मैं के माध्यम से प्रोफ़ाइल का उपयोग नहीं कर रहा हूँ। धन्यवाद।
व्याचे

10

सबसे अच्छा जवाब के अलावा, कुछ स्वचालन जोड़ने के लिए एक अच्छा तरीका ऑटोफैक IoC स्वतंत्रता का उपयोग कर रहा है । इस के साथ आप सिर्फ पहल की परवाह किए बिना अपने प्रोफाइल को परिभाषित।

   public static class MapperConfig
    {
        internal static void Configure()
        {

            var myAssembly = Assembly.GetExecutingAssembly();

            var builder = new ContainerBuilder();

            builder.RegisterAssemblyTypes(myAssembly)
                .Where(t => t.IsSubclassOf(typeof(Profile))).As<Profile>();

            var container = builder.Build();

            using (var scope = container.BeginLifetimeScope())
            {
                var profiles = container.Resolve<IEnumerable<Profile>>();

                foreach (var profile in profiles)
                {
                    Mapper.Initialize(cfg =>
                    {
                        cfg.AddProfile(profile);
                    });                    
                }

            }

        }
    }

और इस लाइन को Application_Startविधि में कॉल करना :

MapperConfig.Configure();

उपरोक्त कोड सभी प्रोफ़ाइल उप वर्गों को ढूँढता है और उन्हें स्वचालित रूप से आरंभ करता है।


7

1 स्थान पर सभी मानचित्रण तर्क रखना मेरे लिए अच्छा अभ्यास नहीं है। क्योंकि मैपिंग क्लास बहुत बड़ी और रखरखाव के लिए बहुत कठिन होगी।

मैं अनुशंसा करता हूं कि मैपिंग सामान को उसी CS फ़ाइल में ViewModel वर्ग के साथ जोड़ दें। आप इस कन्वेंशन का अनुसरण करते हुए आसानी से मैपिंग परिभाषा पर नेविगेट कर सकते हैं। इसके अलावा, मैपिंग क्लास बनाते समय, आप ViewModel गुणों का संदर्भ तेजी से ले सकते हैं क्योंकि वे एक ही फ़ाइल में हैं।

तो आपका व्यू मॉडल क्लास जैसा दिखेगा:

public class UserViewModel
{
    public ObjectId Id { get; set; }

    public string Firstname { get; set; }

    public string Lastname { get; set; }

    public string Email { get; set; }

    public string Password { get; set; }
}

public class UserViewModelMapping : IBootStrapper // Whatever
{
    public void Start()
    {
        Mapper.CreateMap<User, UserViewModel>();
    }
}

9
आप इसे कैसे कहते हैं?
Shawn Mclean

1
मैं एक वर्ग प्रति फ़ाइल नियम का पालन करूंगा: stackoverflow.com/q/2434990/1158845
उमैर

वेलिर के ब्लॉग में इसी तरह के आत्मीयता का वर्णन एमवीसी में ऑटोमैपर के मानचित्र विन्यास का आयोजन है
xmedeko

5

स्थैतिक विधि Mapper.Map () का उपयोग करते हुए AutoMapper के नए संस्करण से हटा दिया गया है। तो आप MapperConfiguration को MvcApplication (Global.asax.cs) पर स्थिर संपत्ति के रूप में जोड़ सकते हैं और इसका उपयोग Mapper की आवृत्ति बनाने के लिए कर सकते हैं।

App_Start

public class MapperConfig
{
    public static MapperConfiguration MapperConfiguration()
    {
        return new MapperConfiguration(_ =>
        {
            _.AddProfile(new FileProfile());
            _.AddProfile(new ChartProfile());
        });
    }
}

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    internal static MapperConfiguration MapperConfiguration { get; private set; }

    protected void Application_Start()
    {
        MapperConfiguration = MapperConfig.MapperConfiguration();
        ...
    }
}

BaseController.cs

    public class BaseController : Controller
    {
        //
        // GET: /Base/
        private IMapper _mapper = null;
        protected IMapper Mapper
        {
            get
            {
                if (_mapper == null) _mapper = MvcApplication.MapperConfiguration.CreateMapper();
                return _mapper;
            }
        }
    }

https://github.com/AutoMapper/AutoMapper/wiki/Migrating-from-static-API


3

जो (खो) का उपयोग कर रहे हैं:

  • वेबएपीआई २
  • सरल इंजेक्टर 3.1
  • ऑटोमैपर 4.2.1 (प्रोफाइल के साथ)

यहां बताया गया है कि मैंने ऑटोमैपर को " नए तरीके " से कैसे एकीकृत किया । इसके अलावा, इस उत्तर के लिए एक बड़ा धन्यवाद (और सवाल)

1 - WebAPI प्रोजेक्ट में "ProfileMappers" नामक एक फ़ोल्डर बनाया गया। इस फोल्डर में मैं अपनी सभी प्रोफाइल क्लासेस देता हूँ जो मेरे मैपिंग बनाता है:

public class EntityToViewModelProfile : Profile
{
    protected override void Configure()
    {
        CreateMap<User, UserViewModel>();
    }

    public override string ProfileName
    {
        get
        {
            return this.GetType().Name;
        }
    }
}

2 - मेरे App_Start में, मेरे पास एक SimpleInjectorApiInitializer है जो मेरे SimpleInjector कंटेनर को कॉन्फ़िगर करता है:

public static Container Initialize(HttpConfiguration httpConfig)
{
    var container = new Container();

    container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();

    //Register Installers
    Register(container);

    container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

    //Verify container
    container.Verify();

    //Set SimpleInjector as the Dependency Resolver for the API
    GlobalConfiguration.Configuration.DependencyResolver =
       new SimpleInjectorWebApiDependencyResolver(container);

    httpConfig.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);

    return container;
}

private static void Register(Container container)
{
     container.Register<ISingleton, Singleton>(Lifestyle.Singleton);

    //Get all my Profiles from the assembly (in my case was the webapi)
    var profiles =  from t in typeof(SimpleInjectorApiInitializer).Assembly.GetTypes()
                    where typeof(Profile).IsAssignableFrom(t)
                    select (Profile)Activator.CreateInstance(t);

    //add all profiles found to the MapperConfiguration
    var config = new MapperConfiguration(cfg =>
    {
        foreach (var profile in profiles)
        {
            cfg.AddProfile(profile);
        }
    });

    //Register IMapper instance in the container.
    container.Register<IMapper>(() => config.CreateMapper(container.GetInstance));

    //If you need the config for LinqProjections, inject also the config
    //container.RegisterSingleton<MapperConfiguration>(config);
}

3 - स्टार्टअप

//Just call the Initialize method on the SimpleInjector class above
var container = SimpleInjectorApiInitializer.Initialize(configuration);

4 - फिर, अपने नियंत्रक में आमतौर पर एक IMapper इंटरफ़ेस के रूप में इंजेक्ट करें:

private readonly IMapper mapper;

public AccountController( IMapper mapper)
{
    this.mapper = mapper;
}

//Using..
var userEntity = mapper.Map<UserViewModel, User>(entity);

कुछ बारीकियों के साथ थोड़ा जुड़ाव के साथ, यह दृष्टिकोण एमवीसी के साथ-साथ धन्यवाद आदमी के साथ उत्कृष्ट रूप से काम करता है!
निक कॉड

कृपया github में एक डेमो उदाहरण जोड़ें
मोहम्मद दल्ली

3

AutoMapper के नए संस्करण (5.x) का उपयोग करके vb.net प्रोग्रामर के लिए।

Global.asax.vb:

Public Class MvcApplication
    Inherits System.Web.HttpApplication

    Protected Sub Application_Start()
        AutoMapperConfiguration.Configure()
    End Sub
End Class

AutoMapperConfiguration:

Imports AutoMapper

Module AutoMapperConfiguration
    Public MapperConfiguration As IMapper
    Public Sub Configure()
        Dim config = New MapperConfiguration(
            Sub(cfg)
                cfg.AddProfile(New UserProfile())
                cfg.AddProfile(New PostProfile())
            End Sub)
        MapperConfiguration = config.CreateMapper()
    End Sub
End Module

प्रोफाइल:

Public Class UserProfile
    Inherits AutoMapper.Profile
    Protected Overrides Sub Configure()
        Me.CreateMap(Of User, UserViewModel)()
    End Sub
End Class

मैपिंग:

Dim ViewUser = MapperConfiguration.Map(Of UserViewModel)(User)

मैंने आपके उत्तर की कोशिश की है, लेकिन यह इस लाइन पर त्रुटि दिखा रहा है: Dim config = New MapperConfiguration (// अधिभार संकल्प विफल रहा क्योंकि कोई भी सुलभ 'नया' इन तर्कों के साथ नहीं कहा जा सकता है: 'सार्वजनिक अधिभार उप नया (कॉन्फ़िगरेशन MapperConfigurationExpression के रूप में कॉन्फ़िगरेशन) कृपया आप मेरी उस पर मदद करें?
बारसन

@ बार्सन: क्या आपने सभी प्रोफ़ाइल कक्षाओं को सही ढंग से कॉन्फ़िगर किया है (UserProfile और PostProfile)? मेरे लिए यह ऑटोमैटर संस्करण 5.2.0 के साथ काम करता है।
रोलाण्ड

नया संस्करण 6.0 जारी किया गया है। इसलिए Protected Overrides Sub Configure()पदावनत कर दिया जाता है। सब कुछ रहता ही है, लेकिन इस लाइन होना चाहिए:Public Sub New()
रॉलेंड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.