I have two classes in my code except one data member everything is same. But, not getting how to resolve this issue using Automapper. Can someone please throw some light on this.
Class A{ public string Id; public List Values; };
Class B{ public string Id; public List Items; };
Class C{ public string Value; public string Name; public string Dept; };
CreateMap<A, B>(); - This is giving following error while doing unit test, Unmapped members were found. Review the types and members below. Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
Explicit AutoMapper Property Mapping
CreateMap<A,B>()
.ForMember(dest => dest.Items,
opt => opt.MapFrom(src => src.Values));
Reference
Related
I have troubles trying to ignore a destination property
Source class:
public class ClassDto
{
public int Id { get; set; }
}
Destination class:
public class ClassModel
{
public int Id { get; set; }
public IList<string> ListString { get; set; }
}
Example:
public class Program
{
static void Main(string[] args)
{
var dto = new ClassDto { Id = 1 };
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ClassDto, ClassModel>().
ForMember(i => i.ListString, opt => opt.DoNotUseDestinationValue());
});
config.AssertConfigurationIsValid();
var mapper = config.CreateMapper();
var model = mapper.Map<ClassDto, ClassModel>(dto);
}
}
Unfortunately config.AssertConfigurationIsValid(); rises an exception:
$exception {"\nUnmapped members were found. Review the types and members below.\nAdd a custom mapping expression, ignore, add a custom
resolver, or modify the source/destination type\nFor no matching
constructor, add a no-arg ctor, add optional arguments, or map all of
the constructor
parameters\n===================================================\r\nClassDto
-> ClassModel (Destination member list)\r\nAutoMapperFoo.ClassDto -> AutoMapperFoo.ClassModel (Destination member list)\r\n\r\nUnmapped
properties:\r\nListString\r\n"} AutoMapper.AutoMapperConfigurationException
I can't undesrtand why, I explicit to ignore ListString by DoNotUseDestinationValue
Thanks in advance
By default, AutoMapper tries to map all the properties from the source
type to the destination type when both source and destination type
property names are the same. If you want some of the properties not to map
with the destination type property then you need to use the AutoMapper
Ignore Property in C#. Learn more AutoMapper Ignore Property in C#
Automapper gives the property Ignore which tells the mapper to not
take the value of a property from the source class. Ignore not only
ignore the mapping for the property, but also ignore the mapping of
all inner properties. It means that if the property is not a primitive
type, but another class, that if you are using Ignore, this class
properties won't be mapped. So, A.B.C would be A.Null.
So try using this:
.ForMember(x => x.ListString, opt => opt.Ignore())
I try to map business objects to persistence objects using expression mapping.
I was able to simplify the problem to this simple usage of AutoMapper.
Classes to map:
public class X
{
public X(string name) => this.Name = name;
public string Name { get; }
}
public class Y
{
public string Name { get; set; }
}
These classes are very simple but shows the problem. X is immutable. That's no problem for AutoMapper. I created a map like:
var mapperConfig = new MapperConfiguration(
cfg =>
{
cfg.CreateMap<X, Y>().ReverseMap();
});
var mapper = mapperConfig.CreateMapper();
This works as expected in this case:
var yInstances = new List<Y> { new Y { Name = "Foo" } }; // Just to have some instances.
var xInstances = yInstances.Select(mapper.Map<X>).ToList();
As expected I got a List of X-Instances.
But I tried to use it as IQueryable<T> using expression mapping:
Expression<Func<X, bool>> filter = item => item.Name == "Foo";
// this line thows exception
yInstances.AsQueryable().Where(mapper.MapExpression<Expression<Func<Y, bool>>>(filter)).ToList();
The last line throws an exception containing this message:
Unhandled exception. AutoMapper.AutoMapperConfigurationException:
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters
=======
Y -> X (None member list)
Y -> X (None member list)
Unmapped properties:
Name
There is no unmapped property because mapper.Map<X> is working.
I tried to add .ForCtorParam("name", opt => opt.MapFrom(src => src.Name)). But this doesn't work - not surprising.
What am I doing wrong? I'm pretty sure that this is only a single step but I'm unable to find it. Sorry.
The Code is on .Net Fiddle that illustrates the problem.
My project compiles and runs - just wont map from one type to another during run time. All code and error message shown below. Does anyone have any ideas / input on this? Thanks in advance!
I have the following code in my startup.cs (configure method) :-
Mapper.Initialize(config =>
{
config.CreateMap<VehicleViewModel, Vehicle>().ReverseMap();
});
which in my mind creates a two way mapping between VehicleViewModel and Vehicle.
the code for VehicleViewMOdel is listed below :-
public class VehicleViewModel
{
//Fuel Economy is in L/Km
public float CityFuelEconomy {get;set;}
public float HighwayFuelEconomy {get;set;}
public float ListPrice {get;set;}
public float SoldPrice{get;set;}
[Required]
public int ModelForeignKey {get;set;}
[Required]
public int DealerForeignKey{get;set;}
public string Notes{get; set;}
public string Color{get; set;}
}
For completeness, the code for Vehicle is listed below :-
public class Vehicle
{
[Key]
public int Id {get; set;}
//Fuel Economy is in L/Km
public float CityFuelEconomy {get;set;}
public float HighwayFuelEconomy {get;set;}
public float ListPrice {get;set;}
public float SoldPrice{get;set;}
[ForeignKey("VehicleModelId")]
public virtual VehicleModels ModelForeignKey {get;set;}
[ForeignKey("VehicleDealerId")]
public virtual Dealer DealerForeignKey{get;set;}
public string Notes{get; set;}
public string Color{get; set;}
}
when the mapping is executed, I see the following message :-
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters
EDIT - as per Hien Nguyen's useful suggestion, I have used ForMember to map the members that are of different types :-
Mapper.Initialize(config =>
{
config.CreateMap<VehicleViewModel, Vehicle>()
.ForMember(dest => dest.DealerForeignKey, opt => opt.MapFrom<DealerResolver>())
.ForMember(dest => dest.ModelForeignKey, opt => opt.MapFrom<VehicleModelResolver>()).ReverseMap();
});
this will use my resolvers to take the relevent Id from the view model and retrieve the correct object that is needed by the model. Here is the code that does the business in the DealerResolver for example :-
public Dealer Resolve(VehicleViewModel source, Vehicle destination, Dealer destMember, ResolutionContext context)
{
List<Dealer> dealers = _vehicleRepo.GetDealerById(source.DealerForeignKey).ToList();
return dealers.FirstOrDefault();
}
I used a very similar idea for the VehicleModelResolver.
So far so good.
The only issue is that when I run the code and try to do the mapping I now see a different error message all together :-
An exception of type 'AutoMapper.AutoMapperMappingException' occurred in AutoMapper.dll
Innermost exception System.MissingMethodException : No parameterless constructor defined for this object
Do you have any idea of what is causing this? I genuinley feel that the orig suggestion of using 'ForMember' is the correct way to go and think we're nearly at a solution for this.
Your model and viewmodel have properties with different type. You can use ForMember to custom mapping between property.
Refer this for custom mapping property
How to handle custom Properties in AutoMapper
I have following structure for tables. The two tables have a lot of common properties over 20 im just listing a two. Also I have 10 tables similar to this. This is how the tables are in the database. There is over 10 concrete tables with similar properties and are not connected to each other in any way. I am using POCO generator to generate the classes from my database.
public class A
{
public string name {get;set;}
public string address {get;set;}
public string AId {get;set;}
}
public class B
{
public string name {get;set;}
public string address {get;set;}
public string BId {get;set;}
}
I have following viewModels:
public class BaseViewModel
{
public string Fullname {get;set;}
public string Fulladdress {get;set;}
}
public class AviewModel : BaseViewModel
{
public string AId {get;set;}
}
public class BViewModel : BaseViewModel
{
public string BId {get;set;}
}
when I create mapping i have to repeat all this for each viewModel that I have created.
config.CreateMap<A, AviewModel>()
.ForMember(dest => Fulladdress, opt => opt.MapFrom(src =>.address))
.ForMember(dest => Fullname, opt => opt.MapFrom(src =>.name)).ReverseMap();
config.CreateMap<B, BviewModel>()
.ForMember(dest => Fulladdress, opt => opt.MapFrom(src =>.address))
.ForMember(dest => Fullname, opt => opt.MapFrom(src =>.name)).ReverseMap();
Is it possible to reduce the repetitive mappings that I might potentially have to do ?
You can move the common mapping code to a helper generic method. You will constrain the TDestination type to be a class derived from BaseViewModel, thus allowing to access the destination members in ForMember method. And for source mapping you will use the MapFrom overload accepting string property name:
public static class CommonMappings
{
public static IMappingExpression<TSource, TDestination> MapToBaseViewModel<TSource, TDestination>(this IMappingExpression<TSource, TDestination> map)
where TDestination : BaseViewModel
{
return map
.ForMember(dest => dest.Fulladdress, opt => opt.MapFrom("address"))
.ForMember(dest => dest.Fullname, opt => opt.MapFrom("name"));
}
}
Then you can use it like this:
config.CreateMap<A, AViewModel>()
.MapToBaseViewModel()
// ... specific mappings
.ReverseMap();
config.CreateMap<B, BViewModel>()
.MapToBaseViewModel()
// ... specific mappings
.ReverseMap();
Update: It turns out that automatic reverse mapping in the latest at this time AutoMapper 6.1.1 works for the lambda overload of MapFrom, but not for the string overload (in AutoMapper 5 it doesn't work at all). So until it gets fixed, you can use the following MapFrom(string) replacement:
public static class AMExtensions
{
public static void From<TSource, TDestination, TMember>(this IMemberConfigurationExpression<TSource, TDestination, TMember> opt, string name)
{
var parameter = Expression.Parameter(typeof(TSource), "src");
var body = Expression.PropertyOrField(parameter, name);
var selector = Expression.Lambda(body, parameter);
opt.MapFrom((dynamic)selector);
}
}
Which means you'll need to replace MapFrom calls in the original solution with From, because we can't give the extension method the same name since it has less priority than the concrete interface method.
Probably too much effort compared to base class approach. But useful in case you can't control the design of the entity classes.
You need a base class for the source classes. You create a map between the base source class and the destination class. You include that map in the map for the derived classes. That would allow you to reuse the configuration. The docs. For simple cases you can use As instead of Include.
You set an attribute on properties on your models.
This contains the name of the property it maps from on a source object.
Then you make a generic method that accepts the target object and source object, that finds the customattribute on the property where the attribute was set, and the property on the target object (or vice versa) and then sets the value.
You can even handle nested objects by asking if its a class property or not.
I have defined a mapping to/from my DTO object, the properties on one versus the other match excatly except that the DTO Object has collections defined as ISet and the non DTO object has those collectiond defined as HashSet . I've noticed a significant performance hit mapping from DTO -> Non DTO, vs the other way.
AutoMapper seems to have trouble going from Interface from concrete class, I'm wondering if I'm missing something in a mapping, or configuration somewhere to be more explicit. This paradigm exists across our code base, but for my object in question I can map 2k objects from the DTO in about 8 seconds, and I can map the exact same objects to the DTO in about .1 seconds
class ExampleDTO
{
public int Id;
public enum Type;
public DateTime creationTime;
public ISet<string> StringThings;
public ISet<int> IntThings;
public ISet<double> DoubleThings;
}
class Example
{
public int Id;
public enum Type;
public DateTime creationTime;
public HashSet<string> StringThings;
public HashSet<int> IntThings;
public HashSet<double> DoubleThings;
}
Mapping:
CreateMap<ExampleDTO, Example>();
CreateMap<Example, ExampleDTO>();
We found that upgrading Automapper (to version 6.0.2) would be the way to go in our case. Using the updated AutoMapper and the same objects and mappings listed above we saw the ExampleDTO->Example object mapped in 1.57 seconds, and the reverse case mapped in 1.86 seconds. I'm not overly happy posting an answer that says use the upgrade, so I'll post a few options that gave some modest gains, and if anyone else has an actual answer, I'll gladly mark that one.
I tried creating a mapping for the ISet HashSet and this was about twice as fast as without that specified mapping, I cannot remember exactly what I did here, but I found it on the googles.
The other option I tried, was creating Non Mappable HashSets on the DTO object that simply returned the ISet. This was about 3x faster, but still not close to the performance gained by upgrading.
class ExampleDTO
{
public int Id;
public enum Type;
public DateTime creationTime;
public ISet<string> StringThings;
[IgnoreMap]
public HashSet<string> StringThingsHash
{
get
{
return StringThings;
}
}
public ISet<int> IntThings;
[IgnoreMap]
public HashSet<int> IntThingsHash
{
get
{
return IntThings;
}
}
public ISet<double> DoubleThings;
[IgnoreMap]
public HashSet<double> DoubleThingsHash
{
get
{
return DoubleThings;
}
}
and I used the following mapping
CreateMap<ExampleDTO, Example>()
.ForMember(dest => dest.StringThings, opts => opts.MapFrom(src => src.StringThingsHash)
.ForMember(dest => dest.IntThings, opts => opts.MapFrom(src => src.IntThingsHash)
.ForMember(dest => dest.DoubleThings, opts => opts.MapFrom(src => src.DoubleThingsHash);
CreateMap<Example, ExampleDTO>();