fluentsprings / ExpressMapper

Mapping .Net types
http://www.expressmapper.org
Other
310 stars 65 forks source link

Ambiguous match found exception when both src and dest types have given property of different types deriving from one another #126

Closed mateubo closed 7 years ago

mateubo commented 7 years ago

Hi Yuriy, Please consider the following scenario (v1.9.0 used):

class A {
}

class AN : A {
}

class T {
   public A Foo { get; set; }
}

class TN : T {
   public new AN Foo { get; set; }
}

It fails with "Ambiguous match found" exception when trying to compile these registrations

Mapper.Register<T, TN>();
Mapper.Register<TN, T>();
Mapper.Compile();

The issue is caused by the usage of Expression.PropertyOrField(...)that throws this exception.

Making these modifications could help:

TypeMapperBase.AutoMapProperty:

var callSetPropMethod = propertySet is PropertyInfo ? Expression.Property(DestFakeParameter, (PropertyInfo)propertySet)
   : propertyGet is FieldInfo ? Expression.Field(DestFakeParameter, (FieldInfo)propertySet)
   : Expression.PropertyOrField(DestFakeParameter, propertySet.Name);

var callGetPropMethod = propertyGet is PropertyInfo ? Expression.Property(SourceParameter, (PropertyInfo)propertyGet)
   : propertyGet is FieldInfo ? Expression.Field(SourceParameter, (FieldInfo)propertyGet)
   : Expression.PropertyOrField(SourceParameter, propertyGet.Name);

TypeMapperBase.ProcessAutoProperties:

foreach (var prop in sourceMembers.GroupBy(x => x.Name).Select(y => y.FirstOrDefault(z => z.DeclaringType == typeof(T)) ?? y.First()))
{
   if (IgnoreMemberList.Contains(prop.Name, comparer) || CustomPropertyCache.Keys.Contains(prop.Name, comparer))
   {
      continue;
   }
   var setpropCandidates = destMembers.Where(x => string.Equals(x.Name, prop.Name, stringComparison));
   var setprop = setpropCandidates.FirstOrDefault(x => x.DeclaringType == typeof(TN)) ?? setpropCandidates.FirstOrDefault();

SourceTypeMapper.CreateQueryableProjection

var destination = autoMember.Value as PropertyInfo;
var fieldsdWithMatchingName = SourceParameter.Type.GetFields().Where(x => x.Name == autoMember.Key.Name);
var propertiesWithMatchingName = SourceParameter.Type.GetProperties().Where(x => x.Name == autoMember.Key.Name);
var fieldInfoFromDeclaringType = fieldsdWithMatchingName.FirstOrDefault(x => x.DeclaringType == SourceParameter.Type);
var propertyInfoFromDeclaringType = propertiesWithMatchingName.FirstOrDefault(x => x.DeclaringType == SourceParameter.Type);

var propertyOrField = propertyInfoFromDeclaringType != null ? Expression.Property(SourceParameter, propertyInfoFromDeclaringType)
    : propertiesWithMatchingName.Any() ? Expression.Property(SourceParameter, propertiesWithMatchingName.First())
    : fieldInfoFromDeclaringType != null ? Expression.Field(SourceParameter, fieldInfoFromDeclaringType)
    : fieldsdWithMatchingName.Any() ? Expression.Field(SourceParameter, fieldsdWithMatchingName.First())
    : Expression.PropertyOrField(SourceParameter, autoMember.Key.Name);
ProcessProjectingMember(propertyOrField, destination);

Thanks!

anisimovyuriy commented 7 years ago

Hi @mateubo ,

Thanks and really appreciate your contribution! Let me do a hot-fix!

anisimovyuriy commented 7 years ago

Fixed and will be included in 1.9.1 release!