Где разместить AutoMapper.CreateMaps?
Я использую AutoMapper на ASP.NET MVC приложение. Мне сказали, что я должен переместить AutoMapper.CreateMap в другом месте, поскольку у них много накладных расходов. Я не слишком уверен, как создать мое приложение, чтобы поместить эти вызовы всего в 1 место.
у меня есть веб-слой, слой сервиса и уровень данных. У каждого свой проект. Я использую Ninject для DI все. Я буду использовать AutoMapper как в веб -, так и в сервисном слоях.
Итак, каковы ваши настройки для AutoMapperCreateMap? Куда ты его положил? Как вы это называете?
10 ответов:
не имеет значения, пока это статический класс. Это все о .
наши это то, что каждый "слой" (веб, сервисы, данные) имеет один файл с именем
AutoMapperXConfiguration.cs, С помощью одного метода под названиемConfigure(), гдеXслой.The
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это похоже на" интерфейс слов " - не может обеспечить его соблюдение, но вы ожидаете его, поэтому вы можете кодировать (и рефакторинг), если это необходимо.
EDIT:
просто подумал, что я упомяну, что теперь я использую 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>(); } }гораздо чище / надежнее.
вы действительно можете поместить его в любом месте, пока ваш веб-проект ссылается на сборку, в которой он находится. В вашей ситуации я бы поместил его на уровень сервиса, поскольку он будет доступен на веб-уровне и на уровне сервиса, а затем, если вы решите сделать консольное приложение или выполняете проект модульного тестирования, конфигурация отображения будет доступна и из этих проектов.
в вашем глобальном.asax затем вы вызовете метод, который устанавливает все ваши карты. Видеть ниже:
Файл AutoMapperBootStrapper.cs
public static class AutoMapperBootStrapper { public static void BootStrap() { AutoMapper.CreateMap<Object1, Object2>(); // So on... } }глобальные.asax при запуске приложения
просто позвони
AutoMapperBootStrapper.BootStrap();настройка Automapper в Bootstrapper нарушает принцип Open-Closed?
обновление: подход, опубликованный здесь, больше не действует как
SelfProfilerбыл удален с AutoMapper v2.Я бы взял аналогичный подход, как Thoai. Но я бы использовал встроенный
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)); } }To инициализируйте в своем приложении, создайте этот класс
public class AutoMapperConfiguration { public static void Initialize() { Mapper.Initialize(x=> { x.SelfConfigure(typeof (UserViewModel).Assembly); // add assemblies as necessary }); } }добавьте эту строку в свой глобальный.асакс.cs файл:
AutoMapperConfiguration.Initialize()теперь вы можете разместить свои классы отображения, где они имеют смысл для вас и не беспокоиться об одном монолитном классе отображения.
для тех из вас, кто придерживаться следующего:
- использование контейнера ioc
- не люблю ломать закрытые для этого
- не нравится монолитный конфигурационный файл
Я сделал комбо между профилями и использованием моего контейнера 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)); } }компромисс заключается в том, что вы должны ссылаться на Mapper с помощью интерфейса IMappingEngine вместо статического Mapper, но это соглашение, с которым я могу жить.
все вышеперечисленные решения предоставляют статический метод для вызова (из app_start или любого другого места), что он должен вызывать другие методы для настройки частей mapping-configuration. Но, если у вас есть модульное приложение, что модули могут подключаться и выходить из приложения в любое время, эти решения не работают. Я предлагаю использовать
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...вы можете установить
WebActivatorчерез NuGet.
В дополнение к лучшему ответу, хороший способ использует Autofac МОК liberary, чтобы добавить некоторую автоматизацию. С этим ты просто определите ваши профили независимо от посвящений.
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();приведенный выше код находит все профиль подклассы и инициировать их автоматически.
размещение всей логики отображения в 1 месте не является хорошей практикой для меня. Потому что класс отображения будет чрезвычайно большим и очень сложным в обслуживании.
Я рекомендую поместить картографический материал вместе с классом ViewModel в тот же файл cs. Вы можете легко перейти к определению отображения, которое вы хотите, следуя этому соглашению. Кроме того, при создании класса сопоставления вы можете быстрее ссылаться на свойства 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>(); } }
из новой версии AutoMapper с использованием статического метода Mapper.Карте() является устаревшим. Таким образом, Вы можете добавить MapperConfiguration как статическое свойство в MvcApplication (Global.асакс.CS) и использовать его для создания экземпляра Картограф.
файл App_Start
public class MapperConfig { public static MapperConfiguration MapperConfiguration() { return new MapperConfiguration(_ => { _.AddProfile(new FileProfile()); _.AddProfile(new ChartProfile()); }); } }глобальные.асакс.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
для vb.net программисты, использующие новую версию (5.x) из AutoMapper.
глобальные.асакс.В. Б.:
Public Class MvcApplication Inherits System.Web.HttpApplication Protected Sub Application_Start() AutoMapperConfiguration.Configure() End Sub End ClassAutoMapperConfiguration:
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)
для тех, кто (потерял), используя:
- WebAPI 2
- SimpleInjector 3.1
- AutoMapper 4.2.1 (С Профилями)
вот как мне удалось интегрировать AutoMapper в "новый путь". Также, а огромный благодаря этому ответ(и вопрос)
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 - автозагрузки.cs
//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);
Comments