AutoMapper throws AutoMapperConfigurationException for one mapping but not for another - automapper

I have a test which calls Mapper.ConfigurationProvider.AssertConfigurationIsValid();
I am getting a AutoMapperConfigurationException when I try to map an IReadOnlyCollection to a List, but only the second time I try to do this.
I am on AutoMapper version 6.0.2. edit: I ran this on the latest version 6.1.1 and still got this error.
I have 4 classes.
FooRow
Foo
BarRow
Bar
I have the following mappings.
CreateMap<FooRow, Foo>
CreateMap<IReadOnlyCollection<FooRow>, List<Foo>>
CreateMap<BarRow, Bar>
CreateMap<IReadOnlyCollection<BarRow>, List<Bar>>
Only #4 fails. I am given the error Unmapped properties: Capacity. Now, obviously for #4 I can add .ForMember(d => d.Capacity, o => o.Ignore) however I am more interested in I must do this for the second mapping of Bar but not Foo.

It might be worth trying to remove the collection mappings, given that AutoMapper has built-in support for arrays and lists:
https://github.com/AutoMapper/AutoMapper/wiki/Lists-and-arrays

Related

Tell AutoMapper that a field is a top-level member

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));

How to get ASTNode definition in JDT?

I can get IBinding from a MethodInvocation.getName() and now I want to get the offset of this binding in the CompilationUnit in order to get the definition position. But I can not find any information of this. By the way, I use ASTParser.setSource(char[]) not IJavaProject.
The normal approach in JDT looks like this:
IJavaElement method= methodBinding.getJavaElement();
if (method instanceof IMember) {
ICompilationUnit cu= ((IMember) method).getCompilationUnit();
CompilationUnit compilationUnit= // use ASTParser here...
ASTNode methodDecl= compilationUnit.findDeclaringNode(methodBinding.getKey());
... methodDecl.getStartPosition() ...
}
This, however, requires that the Java Model is available. If you don't have an IJavaProject then #getJavaElement() will probably answer null. In that case you will have to implement your own heuristic for mapping an ITypeBinding (from IMethodBinding#getDeclaringClass()) to a compilation unit.
Put differently: if you want JDT to help locating elements outside the current compilation unit, then using the Java Model is the way to go.
As an alternative to using the full-blown Java Model, you could try parsing all relevant compilation units in one batch (using #getASTs() - plural), and then create your own reverse map from ITypeBinding to CompilationUnit.

Liferay 7 - Freemarker: unwrap operation not matching the function signature

I am facing an odd problem with Freemarker and the classloader that I did not use to have on 6.2.
Basically, there is a minor logic on the top of the template that uses Oauth. This use to work fine and I can't see a problem with it. I tried placing a variation of the Scribe everywhere I could, and even deleting the one that comes inside the ROOT.
What is odd is that the code successfully calls some methods before the exception is thrown, I guess then it is not a classloader problem but an issue with the unwrap operation. Did something change with regards to that functionality?
Code:
${callbackParameters.add(TrueNTHOAuthConstants.REDIRECT, portalUtil.getCurrentCompleteURL(request))}
<#assign trueNTHConnectLoginURL = trueNTHConnect.getAuthorizationUrl(companyId,1, callbackParameters) /> (Exception at this line)
FreeMarker template error:
No compatible overloaded variation was found; can't convert (unwrap) the 3rd argument to the desired Java type.
The FTL type of the argument values were: number (wrapper: f.t.SimpleNumber), number (wrapper: f.t.SimpleNumber), extended_hash+string (org.scribe.model.ParameterList wrapped into f.e.b.StringModel).
**The matching overload was searched among these members**:
com.sun.proxy.$Proxy799.getAuthorizationUrl(long),
com.sun.proxy.$Proxy799.getAuthorizationUrl(long, int, org.scribe.model.ParameterList, org.scribe.model.ParameterList),
com.sun.proxy.$Proxy799.getAuthorizationUrl(long, int, org.scribe.model.ParameterList)
I have just mentioned the classloader as I had to deal with several ClassNotFoundException or class definition not found to get to this point. This was somehow expected (unpredictable behavior) due to the library replication..
It's possible that you have two different classes loaded with org.scribe.model.ParameterList name. So trueNTHConnect uses another version of the problematic class than the methods called before it. The JVM will see them as totally different incompatible classes, hence there's no matching overload.
There's a sure way to find it out: debug or modify FreeMarker at the places where the class names are printed so that it prints the identity hash of the Class objects too.

Special Groovy magic re property access and collections / iterables?

I understand what is happening here with the spread operator *. in Groovy (2.4.3):
[].class.methods*.name
=> [add, add, remove, remove, get, ...
But why does the leaving the * out produce the same results?
[].class.methods.name
=> [add, add, remove, remove, get, ...
I'd have expected that to be interpreted as accessing the name property of the java.lang.reflect.Method[] returned by methods and so be an error. But it seems to work. Having then experimented a bit more, so do the following:
[*[].class.methods].name
=> [add, add, remove, remove, get, ...
([].class.methods.toList()).name
=> [add, add, remove, remove, get, ...
So it appears attempting to access a property of an array or list (perhaps even Iterable) actually returns a list of that property for each element of the list (as the spread operator would).
So this leaves me wondering:
Is this behaviour documented anywhere? (I don't see it here for example: http://www.groovy-lang.org/operators.html and haven't seen it noted elsewhere in the docs.)
Does this behaviour only apply to 'properties' (i.e. non-arg methods following the getFoo() naming convention)? This seems to be the case from some quick GroovyConsole tests.
Is the spread operator therefore only necessary/useful when calling non-getFoo() style methods or methods with arguments? (Since you can just use . otherwise.)
UPDATE:
It appears to be the case that spread *. works for any Iterable whereas the . only applies to collections. For example:
class Foo implements Iterable {
public Iterator iterator() { [Long.class, String.class].iterator() }
}
(new Foo())*.name
=> [java.lang.Long, java.lang.String]
(new Foo()).name
=> groovy.lang.MissingPropertyException: No such property: name for class: Foo
(I guess this is a good thing: if the Iterable itself later gained a property with the same name, the code would start returning that (single) property from the Iterable - rather than the list property values from the elements.)
That's the GPath expression documented (ish) here, and yes, it only works for properties. (There's an old blog post by Ted Naleid here about digging in to how it worked in 2008)
For methods, you need to use *. or .collect()
See also: Groovy spread-dot operator
Better docs link (as pointed out by #NathanHughes below

Automapping Lists

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.

Resources