اگر مطلب «Refactoring به تزریق وابستگیها» را به خاطر داشته باشید، جهت تشخیص وابستگیهای یک کلاس، کار از بررسی کلمات new و همچنین فراخوانیهای استاتیک، شروع میشود و ... متد استاتیک Mapper.Map کتابخانهی AutoMapper نیز از همین دست است. در ادامه قصد داریم بجای فراخوانی مستقیم Mapper.Map از اینترفیس IMappingEngine به عنوان تامین کنندهی متد Map استفاده کنیم. همچنین کلاسهای Profile نوشته شده را نیز به صورت خودکار به برنامه اضافه نمائیم.
تنظیمات IoC Container مختص به AutoMapper
در ذیل یک کلاس Registry مخصوص StructureMap را مشاهده میکنید که جهت کپسوله کردن اطلاعات خاص AutoMapper تهیه شدهاست. میتوان این اطلاعات را در داخل تنظیمات new Container خود قرار داد و یا میتوان آنها را جهت شلوغ نشدن سایر تنظیمات IoC Container، به یک کلاس Registry منتقل کرد:
هدف اصلی، وهله سازی خودکار IMappingEngine است و برای رسیدن به آن، باید تمام وابستگیهای کلاس MappingEngine را مانند IConfigurationProvider و سایر مواردی که مشاهده میکنید، مشخص کرد. پس از این تنظیمات، کلاس ObjectFactory سفارشی برنامه به شکل ذیل جهت معرفی AutomapperRegistry تغییر خواهد کرد:
در اینجا علاوه بر معرفی AutomapperRegistry، یک مورد دیگر نیز اضافه شدهاست: یافتن خودکار کلاسهایی از نوع Profile و همچنین فراخوانی متد AddProfile کتابخانهی AutoMapper به صورت خودکار. به این ترتیب دیگر نیازی نخواهد بود تا در ابتدای کار برنامه، متد Mapper.Initialize را جهت معرفی کلاسهای Profile فراخوانی کرد و اینکار به صورت خودکار توسط متد configureAutoMapper انجام میشود.
تغییرات لایه سرویس برنامه جهت استفاده از IoC Container
اکنون که IoC Container ما با نحوهی یافتن وابستگیهای IMappingEngine آشنا شدهاست، تنها کافی است این اینترفیس را در سازندهی کلاس سرویس خود تزریق کنیم:
و پس از آن از متد Map این اینترفیس بجای فراخوانی مستقیم Mapper.Map میتوان استفاده کرد. به این ترتیب وابستگی مورد نیاز این کلاس، از طریق سازندهی آن به آن تزریق شدهاست و دیگر فراخوانیهای استاتیک را در اینجا مشاهده نمیکنیم.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید:
AM_Sample03.zip
تنظیمات IoC Container مختص به AutoMapper
در ذیل یک کلاس Registry مخصوص StructureMap را مشاهده میکنید که جهت کپسوله کردن اطلاعات خاص AutoMapper تهیه شدهاست. میتوان این اطلاعات را در داخل تنظیمات new Container خود قرار داد و یا میتوان آنها را جهت شلوغ نشدن سایر تنظیمات IoC Container، به یک کلاس Registry منتقل کرد:
public class AutomapperRegistry : Registry { public AutomapperRegistry() { var platformSpecificRegistry = PlatformAdapter.Resolve<IPlatformSpecificMapperRegistry>(); platformSpecificRegistry.Initialize(); For<ConfigurationStore>().Singleton().Use<ConfigurationStore>() .Ctor<IEnumerable<IObjectMapper>>().Is(MapperRegistry.Mappers); For<IConfigurationProvider>().Use(ctx => ctx.GetInstance<ConfigurationStore>()); For<IConfiguration>().Use(ctx => ctx.GetInstance<ConfigurationStore>()); For<ITypeMapFactory>().Use<TypeMapFactory>(); For<IMappingEngine>().Singleton().Use<MappingEngine>() .SelectConstructor(() => new MappingEngine(null)); this.Scan(scanner => { scanner.AssembliesFromApplicationBaseDirectory(); scanner.ConnectImplementationsToTypesClosing(typeof(ITypeConverter<,>)) .OnAddedPluginTypes(t => t.HybridHttpOrThreadLocalScoped()); scanner.ConnectImplementationsToTypesClosing(typeof(ValueResolver<,>)) .OnAddedPluginTypes(t => t.HybridHttpOrThreadLocalScoped()); }); } }
public static class SmObjectFactory { private static readonly Lazy<Container> _containerBuilder = new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication); public static IContainer Container { get { return _containerBuilder.Value; } } private static Container defaultContainer() { var container = new Container(cfg => { cfg.AddRegistry<AutomapperRegistry>(); cfg.Scan(scan => { scan.TheCallingAssembly(); scan.WithDefaultConventions(); scan.AddAllTypesOf<Profile>().NameBy(item => item.FullName); }); }); configureAutoMapper(container); return container; } private static void configureAutoMapper(IContainer container) { var configuration = container.TryGetInstance<IConfiguration>(); if (configuration == null) return; //saying AutoMapper how to resolve services configuration.ConstructServicesUsing(container.GetInstance); foreach (var profile in container.GetAllInstances<Profile>()) { configuration.AddProfile(profile); } } }
تغییرات لایه سرویس برنامه جهت استفاده از IoC Container
اکنون که IoC Container ما با نحوهی یافتن وابستگیهای IMappingEngine آشنا شدهاست، تنها کافی است این اینترفیس را در سازندهی کلاس سرویس خود تزریق کنیم:
public class UsersService : IUsersService { private readonly IMappingEngine _mappingEngine; public UsersService(IMappingEngine mappingEngine) { _mappingEngine = mappingEngine; } public UserViewModel GetName(int id) { var dbUser1 = new User { Id = 1, Name = "Test", RegistrationDate = DateTime.Now.AddDays(-10) }; var uiUser = new UserViewModel(); _mappingEngine.Map(source: dbUser1, destination: uiUser); return uiUser; } }
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید:
AM_Sample03.zip