I'm starting to learn AutoMapper and coming up against a couple of minor problems.
Essentially I'm getting null reference exceptions when trying to bind to ILists produced by AutoMapper.
My boot strapping method looks like this:
Mapper.CreateMap<Claimant, ClaimantViewModel>()
.ForMember(
vm => vm.Check,
opt => opt.Ignore());
Mapper.CreateMap<IList<Claimant>, IList<ClaimantViewModel>>();
Mapper.AssertConfigurationIsValid();
Which doesn't look to fancy to me. I then try to call:
dlWAMs.DataSource = Mapper.Map<IList<Claimant>, IList<ClaimantViewModel>(someilist);
dlWAMs.DataBind();
With that I'm getting a null reference exception. If I code my own loop and map the models to a view model one at a time the code runs fine.
What am I doing wrong?
First of all you don't need that second map that creates map from IList to IList, remove it. Than if it does not work, show us your classes.
Related
I was going to create an issue on github, but the issue template says I'd rather discuss it here first, so here we go:
I am trying to use Mapstruct to generate mappings from one WSDL-generated set of entities to another. However, without adding a "default" method (manual mapping) it does not work ! That seems to be strange, as I would expect this kind of mapping should not be difficult for Mapstruct. repro case is here: https://github.com/62mkv/wsdl-mapstruct-repro-case
the gist of the code is here:
#Mapper(uses = {
org.system.wsdl.legacy.ObjectFactory.class
})
public interface WsMapper {
org.system.wsdl.legacy.SellGarlicRequest fromCloud(org.system.wsdl.cloud.SellGarlicRequest request);
}
this code above will fail to compile, with such message:
Can't map property "javax.xml.bind.JAXBElement inputParameters" to "javax.xml.bind.JAXBElement inputParameters". Consider to declare/implement a mapping method: "javax.xml.bind.JAXBElement map(javax.xml.bind.JAXBElement value)". org.system.wsdl.legacy.SellGarlicRequest fromCloud(org.system.wsdl.cloud.SellGarlicRequest request);
basically, mapping would go as follows: EntityNew -> JaxbElement -> FieldNew -> FieldOld -> JaxbElement -> EntityOld
as I've read here (https://stackoverflow.com/a/46015381/2583044), mapping from JaxbElement to T is trivial for MapStruct, and to map from T to JaxbElement one has to use "uses" annotation and provide ObjectFactory.class, which I do; however... this seems to not be enough.
if I add these two methods, code compiles good:
org.system.wsdl.legacy.GarlicParameterCollection garlicParameterCollectionToGarlicParameterCollection(org.system.wsdl.cloud.GarlicParameterCollection collection);
default JAXBElement<org.system.wsdl.legacy.GarlicParameterCollection> garlicParameterCollectionToGarlicParameterCollection(JAXBElement<org.system.wsdl.cloud.GarlicParameterCollection> parameterCollectionJAXBElement) {
return new org.system.wsdl.legacy.ObjectFactory().createSellGarlicRequestInputParameters(
this.garlicParameterCollectionToGarlicParameterCollection(parameterCollectionJAXBElement.getValue())
);
}
is it a potential issue in mapstruct or I just don't know how to cook it well?
The problem is that MapStruct sees your object factory method (with argument) as a mapping method. So, it delivers a target, but it has a source as well. If you realise this, then the mapping suddenly is not symmetrical (as it initially appears).
The simple solution is to instruct MapStruct how to handle this.
So: try this:
#Mapper(uses = {
org.system.wsdl.legacy.ObjectFactory.class
})
public interface WsMapper {
org.system.wsdl.legacy.GarlicParameterCollection garlicParameterCollectionToGarlicParameterCollection(org.system.wsdl.cloud.GarlicParameterCollection collection);
#Mapping( target = "inputParameters", source = "inputParameters.value") // this instructs MapStruct to use value of the source JAXBElement (for which it has an object factory method) instead of trying to map JAXBElement to JAXBElement.
org.system.wsdl.legacy.SellGarlicRequest fromCloud(org.system.wsdl.cloud.SellGarlicRequest request);
}
Last but not least, you need to define the first method garlicParameterCollectionToGarlicParameterCollection which surprised me initially.
The reason: MapStruct either tries to:
1. find a mapping method (which is not there if you leave this one out)
or
2. tries to generate a direct mapping (by inspecting if it can find methods for all the attributes on source and target).
However, MapStruct cannot find a direct case for this mapping (it would in principle needs to apply all other possible mappings on its path (e.g. all the methods in the object factory) and then try to generate a mapping method as explained in 2, which could be a lot of combinations. That functionality is not there (and would be load intensive as well I guess).
I have a view model that retrieve a list of data from rest service, and store inside a property.
private readonly ObservableAsPropertyHelper<IReadOnlyReactiveList<Customer>> _searchResultCustomer;
public IReadOnlyReactiveList<Customer> SearchResultCustomer => _searchResultCustomer.Value;
I am trying to do a binding between SearchResultCustomer to ReactiveCollectionView like this this.OneWayBind(ViewModel, vm => vm.SearchResultCustomer, v => v.cvResult.DataSource);.
Without surprise it doesn't work, and the type expected from v.cvResult.DataSource is IUICollectionViewDataSource.
How do I solve this, is there any available example for Xamarin.IOS? Thanks :)
Binding to a UI collection, such as UITableView or UICollectionView, works a bit differently. Conveniently, you don't even need to create your own UICollectionViewSource. Just create a custom cell with an initialize method and bind like this:
ViewModel.WhenAnyValue(vm => vm.SearchResultCustomer).BindTo<Customer, CustomerCell>(collectionView, cell => cell.Initialize());
Credit to Paul Reichelt at this thread who answered a similar question, except for UITableView.
I forked his example project and added a working example for UICollectionView. I use IReactiveList instead of IReadOnlyReactiveList, but you should be able to modify it to fit your needs. Here's the source. Hope it helps.
I am trying to take some of the pain out of creating mapping expressions in AutoMapper, using AutoMapper.QueryableExtensions
I have the following, which gives a critical performance gain:
private MapperConfiguration CreateConfiguration() {
return new MapperConfiguration(cfg => cfg.CreateMap<Widget, WidgetNameDto>()
.ForMember(dto => dto.Name,
conf => conf.MapFrom(w => w.Name)));
}
To understand the performance gain, see here: https://github.com/AutoMapper/AutoMapper/blob/master/docs/Queryable-Extensions.md The key is that the query is limited by field at the database level.
It's terrific that this works. But I anticipate needing to do a lot of this kind of projecting. I am trying to take some of the pain out of the syntax in the ForMember clause above.
For example, I've tried this:
public static IMappingExpression<TFrom, TTo> AddProjection<TFrom, TTo, TField>(this IMappingExpression<TFrom, TTo> expression,
Func<TFrom, TField> from,
Func<TTo, TField> to
)
=> expression.ForMember(t => to(t), conf => conf.MapFrom(f => from(f)));
The problem is that everything I do runs into an error:
AutoMapper.AutoMapperConfigurationException : Custom configuration for members is only supported for top-level individual members on a type.
Even if the passed in Funcs are top-level individual members, that fact is lost in the passing, so I hit the error. I've also tried changing Func<Whatever> to Expression<Func<Whatever>>. It doesn't help.
Is there any way I can simplify the syntax of the ForMember clause? Ideally, I would just pass in the two relevant fields.
First, there is no need to add mapping for the fields/properties that match by name - AutoMapper maps them automatically by convention (that's why it is called convention-based object-object mapper). And for including just some of the properties in the projection you could use the Explicit expansion feature.
Second, what you call a pain in the ForMember syntax is in fact a flexibility. For instance, the explicit expansion and other behaviors can be controlled by conf argument, so it's not only for specifying the source.
With that being said, what you ask is possible. You have to change the from/ to type to Expression:
Expression<Func<TFrom, TField>> from,
Expression<Func<TTo, TField>> to
and the implementation simply as follows:
=> expression.ForMember(to, conf => conf.MapFrom(from));
Does Automapper works with IQueryable?
I have 2 Query
IQueryable<V_ImageUpload> Query1;
IQueryable<V_ImageUpload> Query2;
Mapper.CreateMap<IQueryable<V_ImageUpload_WithReceiptBackup>, IQueryable<V_ImageUpload>>();
Query1 = Mapper.Map<IQueryable<V_ImageUpload_WithReceiptBackup>, IQueryable<V_ImageUpload>>(Query2);
Exception occured is:
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary
I've not used AutoMapper (so this was an excuse to try it out!), but there are queryable extensions available. Both your queryables are of the same type, so I'm not exactly sure what you are trying to achieve, but perhaps something like this is what you want, which takes an IQueryable<V_ImageUpload_WithReceiptBackup> and converts it to an IQueryable<V_ImageUpload>:
IQueryable<V_ImageUpload_WithReceiptBackup> query1;
IQueryable<V_ImageUpload> query2;
// Only map the actual type, not the queryable types
Mapper.CreateMap<V_ImageUpload_WithReceiptBackup, V_ImageUpload>();
query2 = query1.Project().To<V_ImageUpload>();
The .Project().To<V_ImageUpload>() keeps it as IQueryable, while Mapper.Map would end up with a List/IEnumerable. I only tested this out with LINQ to Objects, but hopefully it works with Entity Framework, or whatever you are using.
How to use the function AutoMapper.Mapper.Map(TSource source, Action opts) in AutoMapper ?
I think the 2nd param (Action opts) is there for declaring mapping options if needed but i can't find any API document for this function.
I would like to map 2 objects without having to creatmap first, if the 2 objects have exactly the same fields then the following command works: AutoMapper.Mapper.Map(TSource source)
Please help.
Mapper.DynamicMap<Model, ModelDto>() should do what you want.
I just tested it out and copied over the values from the source to the destination without requiring any CreateMap calls.
For anyone else coming across this older post, Automapper's docs have improved.
You can see examples of using the 2nd parameter for the Map function here:
https://docs.automapper.org/en/latest/Before-and-after-map-actions.html