Gdzie umieścić AutoMapper.CreateMaps?
Używam AutoMapper
w ASP.NET MVC
aplikacji. Powiedziano mi, że powinienem przenieść AutoMapper.CreateMap
gdzie indziej, ponieważ mają dużo kosztów. Nie jestem zbyt pewien, jak zaprojektować moją aplikację, aby umieścić te połączenia w zaledwie 1 miejscu.
Mam warstwę internetową, warstwę usług i warstwę danych. Każdy projekt własny. Używam Ninject
do wszystkiego. Wykorzystam AutoMapper
zarówno w warstwie web jak i service.
Więc jakie są Twoje ustawienia dla AutoMapper
'S CreateMap? Gdzie go położysz? Jak to się nazywa?
10 answers
Nie ma znaczenia, o ile to statyczna Klasa. Chodzi o konwencję .
Nasza konwencja jest taka, że każda "warstwa" (web, services, data) ma jeden plik o nazwie AutoMapperXConfiguration.cs
, z jedną metodą o nazwie Configure()
, Gdzie X
jest warstwą.
Metoda Configure()
następnie wywołuje metody private
dla każdego obszaru.
public static class AutoMapperWebConfiguration
{
public static void Configure()
{
ConfigureUserMapping();
ConfigurePostMapping();
}
private static void ConfigureUserMapping()
{
Mapper.CreateMap<User,UserViewModel>();
}
// ... etc
}
Tworzymy metodę dla każdego "agregatu" (User, Post), więc rzeczy są oddzielone ładnie.
Wtedy twoje Global.asax
:
AutoMapperWebConfiguration.Configure();
AutoMapperServicesConfiguration.Configure();
AutoMapperDomainConfiguration.Configure();
// etc
To coś w rodzaju "interfejsu słów" - nie można go wymusić, ale oczekujesz go, więc możesz kodować (i refaktorować) w razie potrzeby.
EDIT:
Pomyślałem, że wspomnę, że teraz używam profili AutoMapper , więc powyższy przykład staje się:
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>();
}
}
Dużo czystsze/bardziej wytrzymałe.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-18 08:19:08
Możesz umieścić go w dowolnym miejscu, o ile twój projekt internetowy odwołuje się do zestawu, w którym jest. W twojej sytuacji umieściłbym go w warstwie usługowej, ponieważ będzie on dostępny przez warstwę internetową i warstwę usługową, a później, jeśli zdecydujesz się na aplikację konsolową lub wykonujesz projekt testu jednostkowego, konfiguracja mapowania będzie dostępna również z tych projektów.
W Twojej globalnej.asax następnie wywołasz metodę, która ustawia wszystkie Twoje mapy. Patrz poniżej:
Plik AutoMapperBootStrapper.cs
public static class AutoMapperBootStrapper
{
public static void BootStrap()
{
AutoMapper.CreateMap<Object1, Object2>();
// So on...
}
}
Globalny.asax przy uruchomieniu aplikacji
Po Prostu zadzwoń
AutoMapperBootStrapper.BootStrap();
Teraz niektórzy ludzie będą argumentować przeciwko tej metodzie narusza pewne solidne zasady, które mają ważne argumenty. Oto one do czytania.
Konfigurowanie Automapp w Bootstrapperze narusza zasadę Open-Closed?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:55
Update: podejście opublikowane tutaj nie jest już ważne, ponieważ SelfProfiler
zostało usunięte z AutoMapper v2.
Przyjąłbym podobne podejście jak Thoai. Ale użyłbym wbudowanej klasy SelfProfiler<>
do obsługi map, a następnie użyłbym funkcji Mapper.SelfConfigure
do inicjalizacji.
Użycie tego obiektu jako źródła:
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);
}
}
I te jako miejsce przeznaczenia:
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; }
}
Możesz tworzyć te profile:
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));
}
}
Aby zainicjować w aplikacji, Utwórz ten Klasa
public class AutoMapperConfiguration
{
public static void Initialize()
{
Mapper.Initialize(x=>
{
x.SelfConfigure(typeof (UserViewModel).Assembly);
// add assemblies as necessary
});
}
}
Dodaj tę linię do swojego globalnego.asax.plik cs: AutoMapperConfiguration.Initialize()
Teraz możesz umieścić swoje klasy mapowania tam, gdzie mają dla ciebie sens i nie martwić się o jedną monolityczną klasę mapowania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-24 17:00:19
Dla tych z Was, którzy przestrzegają następujących zasad:
- używanie kontenera ioc
- don ' t like to break open closed for this
- nie lubię monolitycznego pliku konfiguracyjnego
Zrobiłem kombinację między profilami i wykorzystałem mój kontener ioc:
Konfiguracja 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;
}));
}
}
Przykład konfiguracji:
public class TagStatusViewModelMappings : Profile
{
protected override void Configure()
{
Mapper.CreateMap<Service.Contracts.TagStatusViewModel, TagStatusViewModel>();
}
}
Przykład użycia:
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));
}
}
Kompromis polega na tym, że musisz odwołać się do Mapera przez interfejs IMappingEngine zamiast statycznego Mapera, ale z takim konwentem mogę żyć.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-24 17:01:23
Wszystkie powyższe rozwiązania zapewniają statyczną metodę wywołania (z app_start lub dowolnego miejsca), która powinna wywoływać inne metody w celu skonfigurowania części mapowania-konfiguracji. Jeśli jednak masz modułową aplikację, która może w dowolnym momencie zostać podłączona i wyłączona z aplikacji, rozwiązania te nie działają. Sugeruję użycie biblioteki WebActivator
, która może zarejestrować niektóre metody do uruchomienia na app_pre_start
i app_post_start
w dowolnym miejscu:
// 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...
Możesz zainstalować WebActivator
poprzez NuGet.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-14 16:50:59
Oprócz najlepszej odpowiedzi, dobrym sposobem jest użycie Autofac IoC liberary, aby dodać trochę automatyzacji. Dzięki temu możesz po prostu zdefiniować swoje profile niezależnie od inicjacji.
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);
});
}
}
}
}
I wywołanie tej linii w metodzie Application_Start
:
MapperConfig.Configure();
Powyższy kod znajduje wszystkieProfile podklasy i inicjuje je automatycznie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-27 13:39:25
Umieszczenie całej logiki mapowania w jednym miejscu nie jest dla mnie dobrą praktyką. Ponieważ Klasa mapowania będzie bardzo duża i bardzo trudna do utrzymania.
Polecam umieścić mapowanie razem z klasą ViewModel w tym samym pliku cs. Możesz łatwo przejść do definicji mapowania zgodnie z tą konwencją. Co więcej, podczas tworzenia klasy mapowania można szybciej odwoływać się do właściwości ViewModel, ponieważ znajdują się one w tym samym pliku.
Więc twój pogląd Klasa modelu będzie wyglądać następująco:
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>();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-27 04:23:58
Z nowej wersji Automapp przy użyciu statycznej metody Mapper.Map() jest przestarzała. Możesz więc dodać MapperConfiguration jako właściwość statyczną do MvcApplication (Global.asax.cs) i używać go do tworzenia instancji 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-20 11:56:26
Dla vb.net Programiści korzystający z nowej wersji (5.x) Automapp.
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
Profile:
Public Class UserProfile
Inherits AutoMapper.Profile
Protected Overrides Sub Configure()
Me.CreateMap(Of User, UserViewModel)()
End Sub
End Class
Mapowanie:
Dim ViewUser = MapperConfiguration.Map(Of UserViewModel)(User)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-21 11:27:41
Dla tych, którzy (zagubieni) używają:
- WebAPI 2
- SimpleInjector 3.1
- AutoMapper 4.2.1 (Z Profilami)
Oto jak udało mi się zintegrować AutoMapper w " new way". Również, a Ogromne dzięki temu odpowiedź (i pytanie)
1-Utworzono folder w projekcie WebAPI o nazwie "ProfileMappers". W tym folderze umieszczam wszystkie moje klasy profili, które tworzą moje mapowania:
public class EntityToViewModelProfile : Profile
{
protected override void Configure()
{
CreateMap<User, UserViewModel>();
}
public override string ProfileName
{
get
{
return this.GetType().Name;
}
}
}
2-w moim App_Start, I posiadaj SimpleInjectorApiInitializer, który konfiguruje mój kontener 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-Start.cs
//Just call the Initialize method on the SimpleInjector class above
var container = SimpleInjectorApiInitializer.Initialize(configuration);
4-Następnie w kontrolerze po prostu wstrzyknij jak zwykle interfejs IMapper:
private readonly IMapper mapper;
public AccountController( IMapper mapper)
{
this.mapper = mapper;
}
//Using..
var userEntity = mapper.Map<UserViewModel, User>(entity);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:35