I am using static class for mapping my entities. But if I use the following code, it is not working for converting lists and nested entities;
public static class MapperUtil<TSource, TDestination>
{
private static readonly Mapper _mapper = new Mapper(new MapperConfiguration(
cfg =>
{
cfg.CreateMap<TDestination,TSource>().ReverseMap();
}));
public static TDestination Map(TSource source)
{
return _mapper.Map<TSource,TDestination>(source);
}
}
But if I use the following code it works well.
var mapper = new Mapper(new MapperConfiguration(cfg =>
{
cfg.CreateMap<List<User>, List<UserDto>>().ReverseMap();
}));
List<UserDto> userDto = mapper.Map<List<User>,List<UserDto>> (users);
Can anyone help me? (I am newbie).
And is it good idea using static class for mapping? What is your solution for mapping as static class?
You should remove List in CreateMap method and create map for your types:
var mapper = new Mapper(new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDto>().ReverseMap();
}));
Finally:
List<UserDto> userDto = mapper.Map<List<UserDto>>(users);
if you are using generic type for mapping, try below code
public class Source<T> {
public T Value { get; set; }
}
public class Destination<T> {
public T Value { get; set; }
}
// Create the mapping
var configuration = new MapperConfiguration(cfg => cfg.CreateMap(typeof(Source<>), typeof(Destination<>)));
Related
First of all, sorry my English :)
I am using AutoMapper to map between classes.
I have structure of classes like the below.
public class OrderDto {
int Id { get; set; }
}
public class OrderDtoList {
OrderDto[] Orders { get; set; }
}
public class Order {
int Id { get; set; }
}
My issue is that, I want to map OrderDtoList object to List using AutoMapping Profile.
public class OrderMappingProfile : Profile {
public OrderMappingProfile() {
CreateMap<OrderDto, Order>();
CreateMap<OrderDtoList, List<Order>(); // <<<<<<<< I cannot figure out this.
}
}
But I cannot figure out how to write CreateMap<>() function.
Please help.
You can create a class which implements ITypeConverter< OrderDtoList, List > and create the mapping using ConvertUsing method.
public class OrderDtoListMapper : ITypeConverter<OrderDtoList, List<Order>>
{
public List<Order> Convert(OrderDtoList source, List<Order> destination, ResolutionContext context)
{
return context.Mapper.Map<List<Order>>(source.Orders);
}
}
Then, you can create your mapping like this:
public class MapperProfile : Profile
{
public MapperProfile()
{
CreateMap<OrderDto, Order>();
CreateMap<OrderDtoList, List<Order>>().ConvertUsing<OrderDtoListMapper>();
}
}
Hope this is what you were looking for!
It's a little difficult to get it from scratch, but it is possible. You have to create a ITypeConverter<,> and apply this to be used for the conversion.
Be aware, that the ConvertUsing() method has also an overload to simply add an inline function, but you need to have access to the mapper itself to call it for mapping the inner objects to the desired result objects and that's only available with the type converter interface.
public class OrderMappingProfile : Profile
{
public OrderMappingProfile()
{
CreateMap<OrderDto, Order>();
CreateMap<OrderDtoList, List<Order>>().ConvertUsing<CustomConverter>();
}
}
public class CustomConverter : ITypeConverter<OrderDtoList, List<Order>>
{
public List<Order> Convert(OrderDtoList source, List<Order> destination, ResolutionContext context)
{
return context.Mapper.Map<List<Order>>(source.Orders);
}
}
With this in place, you can create the desired list right from the DTO:
public static class Program
{
public static void Main()
{
var config = new MapperConfiguration(cfg => cfg.AddProfile<OrderMappingProfile>());
var mapper = config.CreateMapper();
var orderList = new OrderDtoList { Orders = Enumerable.Range(1, 10).Select(i => new OrderDto { Id = i }).ToArray() };
var orders = mapper.Map<List<Order>>(orderList);
}
}
As Lucian mentioned, there is an overload of ConvertUsing() that provides the context. So you could also inline this, instead of using an own class:
// Use lambda method
CreateMap<OrderDtoList, List<Order>>()
.ConvertUsing((source, _, context) => context.Mapper.Map<List<Order>>(source.Orders));
// Use static method
CreateMap<OrderDtoList, List<Order>>().ConvertUsing(ListConverter);
private static List<Order> ListConverter(OrderDtoList source, List<Order> destination, ResolutionContext context)
{
return context.Mapper.Map<List<Order>>(source.Orders);
}
below is a generic extension, if you have not used an extension before please observe that the GenericExtensions class is a static class
public static class GenericExtensions {
public static object Map<T>(this T source)
{
var fullName = source.GetType().FullName;
var sourceType = source.GetType();
var baseType = ObjectContext.GetObjectType(source.GetType());
var config = new MapperConfiguration(cfg =>
cfg.CreateMap(sourceType, baseType));
var mapper = config.CreateMapper();
var entity = mapper.Map(source, sourceType, baseType);
return entity;
}
}
public static List<T> Map<T>(this List<T> original)
{
var config = new MapperConfiguration(cfg =>
cfg.CreateMap(typeof(T), typeof(T)));
var mapper = config.CreateMapper();
return original.Select(mapper.Map<T, T>).ToList();
}
Usage:
for single entity:
var originalObject = new Order();
originalObject.Id = 4;
var clonedObject = originalObject.Map();
for list of entities:
var objectList = db.ORDERS.ToList();
var clonedList = objectList.Map();
Hope this helps!
I currently have 4 classes that inherits Buildable class. For each derived class, I have to set it's position every time I inherits the buildable class. Is there any way to make this more cleaner? I think showing the code will make it more easier to understand.
public class BuildableData
{
public Vector3 position;
}
public class StockpileData : BuildableData
{
public int woodCount = 0;
public static StockpileData Create(Stockpile stockpile)
{
return new StockpileData
{
position = stockpile.transform.position,
woodCount = stockpile.WoodCount
};
}
}
public class HouseData : BuildableData
{
public static HouseData Create(House house)
{
return new HouseData
{
position = house.transform.position, // I'm talking about this one? I have to set it everytime I inherit BuildableData
};
}
}
Is there any way to make it automatically set by just passing the object to the constructor or maybe using reflection?
AutoMapper is the right library here to solve the mentioned problem as it helps to map one object's properties to another object's properties. Below code snippet will help to configure the same in your project.
using AutoMapper;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingProfile());
});
var mapper = mappingConfig.CreateMapper();
var house = new House();
var houseData = mapper.Map<HouseData>(house);
var stockpile = new Stockpile();
var stockpileData = mapper.Map<StockpileData>(stockpile);
}
}
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<House, HouseData>()
.ForMember(destination => destination.Position,
source => source.MapFrom(m => m.transform.Position));
CreateMap<Stockpile, StockpileData>()
.ForMember(destination => destination.Position,
source => source.MapFrom(m => m.transform.Position));
}
}
}
I am using the following mapping to map my data object to viewmodel object.
ObjectMapper.cs
public static class ObjectMapper
{
public static void Configure()
{
Mapper.CreateMap<User, UserViewModel>()
.ForMember(dest => dest.Title,
opt => opt.ResolveUsing<TitleValueResolver>())
.ForMember(dest => dest.Name,
opt => opt.ResolveUsing<NameValueResolver >())
.ForMember(dest => dest.ShortName,
opt => opt.ResolveUsing<ShortNameValueResolver >());
}
}
Parser
public class Parser{
public string GetTitle(string title){
/* add some logic */
return title;
}
public string GetName(string title){
/* add some logic */
return name;
}
public string GetShortName(string title){
/* add some logic */
return shortname;
}
}
AutoMapperCustomResolvers.cs
public class TitleValueResolver : ValueResolver<User, string>
{
private readonly BaseValueResolver _baseResolver;
public TitleValueResolver()
{
_baseResolver = new BaseValueResolver();
}
protected override string ResolveCore(Usersource)
{
return _baseResolver.Parser.GetTitle(source.TITLE);
}
}
public class NameValueResolver : ValueResolver<User, string>
{
private readonly BaseValueResolver _baseResolver;
public NameValueResolver()
{
_baseResolver = new BaseValueResolver();
}
protected override string ResolveCore(Usersource)
{
return _baseResolver.Parser.GetName(source.TITLE);
}
}
public class ShortNameValueResolver : ValueResolver<User, string>
{
private readonly BaseValueResolver _baseResolver;
public ShortNameValueResolver()
{
_baseResolver = new BaseValueResolver();
}
protected override string ResolveCore(Usersource)
{
return _baseResolver.Parser.GetShortName(source.TITLE);
}
}
I am using the above code to add logic to the destination property using the separate custom value resolvers. Not sure is this the right approach.
i) Is there a better way to achieve this?
ii) And how to use unity to resolve in case i want to inject some dependency to custom resolver constructor?
Thanks
As I understand your question, you want to utilize a ValueResolver, that resolves multiple source properties into an intermediate data object, which is used to resolve multiple target properties. As an example, I assume the following source, target, intermediate and resolver types:
// source
class User
{
public string UserTitle { get; set; }
}
// target
class UserViewModel
{
public string VM_Title { get; set; }
public string VM_OtherValue { get; set; }
}
// intermediate from ValueResolver
class UserTitleParserResult
{
public string TransferTitle { get; set; }
}
class TypeValueResolver : ValueResolver<User, UserTitleParserResult>
{
protected override UserTitleParserResult ResolveCore(User source)
{
return new UserTitleParserResult { TransferTitle = source.UserTitle };
}
}
You need a target property in order to utilize opt.ResolveUsing<TypeValueResolver>(). This means, you can establish a mapping, where an appropriate target property is available.
So, for the moment, lets wrap the result into an appropriate container type:
class Container<TType>
{
public TType Value { get; set; }
}
And create a mapping
Mapper.CreateMap<User, Container<UserViewModel>>()
.ForMember(d => d.Value, c => c.ResolveUsing<TypeValueResolver>());
And another mapping
Mapper.CreateMap<UserTitleParserResult, UserViewModel>()
.ForMember(d => d.VM_Title, c => c.MapFrom(s => s.TransferTitle))
.ForMember(d => d.VM_OtherValue, c => c.Ignore());
And another mapping
Mapper.CreateMap<User, UserViewModel>()
.BeforeMap((s, d) =>
{
Mapper.Map<User, Container<UserViewModel>>(s, new Container<UserViewModel> { Value = d });
})
.ForAllMembers(c => c.Ignore());
// establish more rules for properties...
The last mapping is a bit special, since it relies on a nested mapping in order to update the destination with values from source via separately configured mapping rules. You can have multiple different transfer mappings for different properties by adding appropriate intermediate mappings and calls in BeforeMap of the actual mapped type. The properties that are handled in other mappings need to be ignored, since AutoMapper doesn't know about the mapping in BeforeMap
Small usage example:
var user = new User() { UserTitle = "User 1" };
// create by mapping
UserViewModel vm1 = Mapper.Map<UserViewModel>(user);
UserViewModel vm2 = new UserViewModel() { VM_Title = "Title 2", VM_OtherValue = "Value 2" };
// map source properties into existing target
Mapper.Map(user, vm2);
Dunno if this helps you. There might be better ways if you rephrase your question to describe your initial problem instead of what you suspect to be a solution.
I have a ASP.NET 5 (running on 4.6.2, not Core) application. I wanted to use the ProjectTo<>() method of AutoMapper to project the results from the database to my viewmodels.
I've tried alot of tests, but it seems that the map solely cannot be found when using the ProjectTo<>(). Using mapper.Map<>() on different locations with the same model and viewmodel perfectly works.
I guess there is something wrong with how AutoMapper works with my DI (Autofac), but I can't figure out what.
Anyway, the code:
Startup.Cs
public IServiceProvider ConfigureServices(IServiceCollection services)
{
(...)
// Autofac DI
AutofacContainer = AutofacLoader.Configure(services).Build();
return AutofacContainer.Resolve<IServiceProvider>();
}
AutofacLoader.cs
public static ContainerBuilder Configure(IServiceCollection services)
{
var builder = new ContainerBuilder();
(...)
// AutoMapper
builder.RegisterModule<AutoMapperModule>();
if (services != null)
{
builder.Populate(services);
}
return builder;
}
AutoMapperModule.cs
public class AutoMapperModule : Module
{
protected override void Load(ContainerBuilder builder)
{
var mapping = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new Core.Mappings.AutoMapperProfileConfiguration());
cfg.AddProfile(new Dieet.Core.Mappings.AutoMapperProfileConfiguration());
});
builder.RegisterInstance(mapping.CreateMapper()).As<IMapper>().AutoActivate();
}
}
The test that fails with 'Missing map from Patient to PatientViewModel. Create using Mapper.CreateMap'.
[Fact]
public async void InfohosServiceReturnsPatientViewModels()
{
var db = _container.Resolve<IInfohosDb>();
var search = new PaginatedSearchBase();
search.OrderBy = "Naam";
var mapper = _container.Resolve<IMapper>();
var result = await search.PagedResultAsAsync<Patient,PatientViewModel >(null,db.Patienten,mapper);
}
PaginatedSearchBase
public class PaginatedSearchBase
{
public string OrderBy { get; set; }
public bool OrderDescending { get; set; }
public int Page { get; set; } = 1;
public int PageSize { get; set; } = 10;
}
And finally the extension that calls the ProjectTo
public static class PagedResultExtensions
{
public static async Task<PagedResult<T>> PagedResultAsync<T>(this PaginatedSearchBase vm, ICollection<Expression<Func<T, bool>>> whereCollection, IEnumerable<T> context) where T : class
{
int totalCount;
var query = PrepareQuery(vm, whereCollection, context, out totalCount);
return new PagedResult<T>
{
Results = await query.ToListAsync(),
Page = vm.Page,
PageSize = vm.PageSize,
Total = totalCount
};
}
public static async Task<PagedResult<TAs>> PagedResultAsAsync<T, TAs>(this PaginatedSearchBase vm, ICollection<Expression<Func<T, bool>>> whereCollection, IEnumerable<T> context, IMapper mapper) where T : class
{
int totalCount;
var query = PrepareQuery(vm, whereCollection, context, out totalCount);
return new PagedResult<TAs>
{
----------> Results = await query.ProjectTo<TAs>(mapper).ToListAsync(),
Page = vm.Page,
PageSize = vm.PageSize,
Total = totalCount
};
}
private static IQueryable<T> PrepareQuery<T>(PaginatedSearchBase vm, ICollection<Expression<Func<T, bool>>> whereCollection, IEnumerable<T> context,
out int totalCount) where T : class
{
var query = context.AsQueryable();
if (whereCollection != null)
{
foreach (var w in whereCollection)
{
if (w != null)
{
query = query.Where(w);
}
}
}
// Order by
query = query.OrderBy($"{vm.OrderBy} {(vm.OrderDescending ? "DESC" : "ASC")}");
// Total rows
totalCount = query.Count();
// Paging
query = query.Skip((vm.Page - 1)*vm.PageSize).Take(vm.PageSize);
return query;
}
}
For information, I'm using versions:
"Autofac": "4.0.0-rc1-177"
"Autofac.Extensions.DependencyInjection": "4.0.0-rc1-177"
"AutoMapper": "4.2.1"
Edit:
A new test that I did to check if the mappings really work:
var mapper = _container.Resolve<IMapper>();
var p = new Patient();
p.Naam = "Test";
var vm = mapper.Map<PatientViewModel>(p);
vm.Naam.ShouldBeEquivalentTo("Test");
This test passes
Edit 2:
When I use the Map<> in a Select() instead, it works too, so it's really the ProjectTo<>() that fails:
var results = await query.ToListAsync();
return new PagedResult<TAs>
{
Results = results.Select(mapper.Map<TAs>).ToList(),
Page = vm.Page,
PageSize = vm.PageSize,
Total = totalCount
};
This works, but it requires the mapper to be included and not be injected and it doesn't use the ProjectTo that Automapper has for database access...
I ran into the same issue but managed to get it working. This is happening due to the recent move, by Automapper, away from having the entire API use static methods. Now that everything is instance-based, the static extension methods no longer know about the mapping configuration and they now have to be passed into the method. I ended up registering an instance of the MapperConfiguration as IConfigurationProvider (I'm using Unity)
container.RegisterInstance(typeof (IConfigurationProvider), config);
This is injected into my query handler:
[Dependency]
public IConfigurationProvider MapperConfigurationProvider { get; set; }
Finally, the MapperConfigurationProvider is passed to the call to ProjectTo:
.ProjectTo<Payment>(MapperConfigurationProvider);
Hope this helps.
You don't have to specifically add ConfigurationProvider to DI. If you already added the IMapper to DI, than you can read ConfigurationProvider from the Mapper itself. Example: I had the same problem and created a base class containing IMapper that got injected:
public abstract class ServiceBase
{
public IMapper Mapper { get; set; }
}
This class was inherited in all my Services that used AutoMapper. Now every time any of my services needed to Map something, they did so:
return context.SomeEntity
.Where(e => e.Id == filter.Id)
.ProjectTo<EntityDto>(Mapper.ConfigurationProvider).ToList();
With Mapper being injected.
As long as you put the completely configured Mapper in DI, you're ok.
container.Register(Component.For<IMapper>().UsingFactoryMethod(x =>
{
return new AutoMapperConfig().ConfigureMapper();
})
);
This is what I am currently using (simplified and run in console app):
public class SomeValueResolver : ValueResolver<DateTime, long>
{
private readonly ISomeDependency _someDependency;
public SomeValueResolver(ISomeDependency _someDependency)
{
// ...
}
protected override long ResolveCore(DateTime source)
{
// ...
}
}
public class MyRegistry : Registry
{
public MyRegistry()
{
For<ISomeDependency >()
.Singleton()
.Use<SomeDependency>();
}
}
public static class AutoMapperConfiguration
{
public static void Configure(IContainer container)
{
Mapper.Initialize(cfg =>
{
cfg.ConstructServicesUsing(t => container.GetInstance(t));
cfg.AddProfile(new AutomapperProfile1());
});
}
}
public class AutomapperProfile1 : Profile
{
protected override void Configure()
{
CreateMap<Source, Target>()
.ForMember(dest => dest.Y, opt => opt.ResolveUsing<SomeValueResolver>().FromMember(e => e.X))
.IgnoreAllSourcePropertiesWithAnInaccessibleSetter();
}
}
public class Source
{
public DateTime X { get; set; }
}
public class Target
{
public DateTime Y { get; set; }
}
// main method
var container1 = new Container(new MyRegistry());
AutoMapperConfiguration.Configure(container1);
var source = new Source { X = DateTime.UtcNow };
var target = Mapper.Map<Target>(source);
Unfortunately, I get an exception along those lines:
Unable to create a build plan for concrete type SomeValueResolver
new SomeValueResolver(ISomeDependency)
? ISomeDependency= **Default**
1.) Attempting to create a BuildPlan for Instance of SomeValueResolver -- SomeValueResolver
2.) Container.GetInstance(SomeValueResolver)
Can this be resolved (pardon the pun).
I've tried your code with StructureMap 4.0.1.318 and Automapper 4.2.0.0.
I did get an exception which is different because related to bad conversion of DateTime to Int64.
I think you intended to write this :
public class Target
{
public long Y { get; set; }
}
By changing the type, the mapping works like a charm.
It's maybe related to the SomeDependency class which should possess a parameterless constructor to be resolved that way.