// TODO: Consider doing this at model building time, but also consider mapping to interfaces public virtual IEnumerable <Tuple <IProperty, MemberInfo> > MapPropertiesToMembers([NotNull] IEntityType entityType) { Check.NotNull(entityType, "entityType"); var fieldCache = new Dictionary <Type, Dictionary <string, FieldInfo> >(); var propertyMappings = new List <Tuple <IProperty, MemberInfo> >(); foreach (var property in entityType.Properties.Where(p => p.IsClrProperty)) { var propertyName = property.Name; MemberInfo memberInfo = null; foreach (var propertyInfo in entityType.Type.GetPropertiesInHierarchy(propertyName)) { // TODO: Handle cases where backing field is declared in a different class than the property Dictionary <string, FieldInfo> fields; if (!fieldCache.TryGetValue(propertyInfo.DeclaringType, out fields)) { fields = propertyInfo.DeclaringType.GetRuntimeFields().ToDictionary(f => f.Name); fieldCache[propertyInfo.DeclaringType] = fields; } var fieldName = property["BackingField"]; if (fieldName != null) { FieldInfo fieldInfo; if (!fields.TryGetValue(fieldName, out fieldInfo)) { throw new InvalidOperationException(Strings.FormatMissingBackingField(entityType.Name, propertyName, fieldName)); } if (!fieldInfo.FieldType.GetTypeInfo().IsAssignableFrom(property.PropertyType.GetTypeInfo())) { throw new InvalidOperationException( Strings.FormatBadBackingFieldType(fieldName, fieldInfo.FieldType.Name, entityType.Name, propertyName, property.PropertyType.Name)); } memberInfo = fieldInfo; } else { memberInfo = _fieldMatcher.TryMatchFieldName(property, propertyInfo, fields); } if (memberInfo != null) { break; } } if (memberInfo == null) { memberInfo = entityType.Type.GetPropertiesInHierarchy(propertyName).FirstOrDefault(p => p.SetMethod != null); } if (memberInfo == null) { throw new InvalidOperationException(Strings.FormatNoFieldOrSetter(entityType.Name, propertyName)); } propertyMappings.Add(Tuple.Create(property, memberInfo)); } return(propertyMappings); }