/// <summary> /// Determine if the member value should be mapped from a rule, if true, then the value of 'value' will be set to the custom mapping /// </summary> /// <param name="info"></param> /// <param name="expression"></param> /// <param name="value"></param> /// <returns></returns> internal bool MapFromRule(PropertyMapDefinition info, MemberExpressionSignature expression, out object value) { //find the expression in our list based on the target object type, the target property type and the target property name var action = (from m in _memberExpressionSignatures where m.Equals(expression) select m.ActionToExecute).SingleOrDefault(); if (action != null) { //invoke the action with the specified parameter var memberExpression = new MemberMappingExpression <TSource>(info); action.Invoke(memberExpression); if (!memberExpression.IsIgnored) { //set the value to the output value = memberExpression.ResultOfMapFrom; return(true); } } //no 'MapFrom' found value = null; return(false); }
public MemberMappingExpression(PropertyMapDefinition info) { _info = info; ResultOfMapFrom = null; }
/// <summary> /// Determines whether the member is already mapped /// </summary> /// <param name="member">The member.</param> /// <returns> /// <c>true</c> if [is already mapped] [the specified member]; otherwise, <c>false</c>. /// </returns> protected bool IsAlreadyMapped(PropertyMapDefinition member) { return(_memberMapped.Contains(member.GetTargetObjectHashCode())); }
/// <summary> /// Adds the hash code of the member that has been mapped /// </summary> /// <param name="member">The member to get the hashcode from</param> protected void AddMemberMapped(PropertyMapDefinition member) { _memberMapped.Add(member.GetTargetObjectHashCode()); }
public void Map(object source, object target, MappingExecutionScope scope) { var ci = new PropertyMapDefinition { Source = { Type = source.GetType(), Value = source }, Target = { Type = target.GetType(), Value = target } }; var sourceProps = source.GetProperties(); var targetProps = target.GetProperties(); //create MemberExpressionSignature for each property var targetPropDefs = targetProps.Cast <PropertyDescriptor>() .Select(x => new { expression = new MemberExpressionSignature( x.PropertyType, typeof(TTarget), x.Name), property = x }).ToArray(); //before we map the members by name, we need to ignore any members that have been referenced in the member expressions var filteredTargetProps = new PropertyDescriptorCollection( (from t in targetPropDefs where !_mapper.MappingContext.MemberExpressions.Any(x => x.Equals(t.expression)) select t.property).ToArray()); foreach (PropertyDescriptor s in sourceProps) { var t = filteredTargetProps.Find(s.Name, false); if (t == null) { continue; } var reflectedProp = ci.Target.Type.GetProperty(t.Name); if (reflectedProp == null) { continue; } if (!reflectedProp.CanWrite) { continue; } ci.SourceProp.Name = s.Name; ci.SourceProp.Value = s.GetValue(source); ci.SourceProp.Type = s.PropertyType; ci.TargetProp.Name = t.Name; ci.TargetProp.Type = t.PropertyType; ci.TargetProp.Value = t.GetValue(target); //if people have overridden this injector, we'll need to check that this hasn't already been mapped if (IsAlreadyMapped(ci)) { continue; } //check that we can proceed with setting this value if (IsTypeMappable(s.PropertyType, t.PropertyType)) { //set the output value to the source ci.TargetProp.Value = ci.SourceProp.Value; } else { var hasConverted = false; // If no explicit mapping exists, but a TypeConverter exists between the source & destination types, use that if (TypeFinder.IsImplicitValueType(ci.TargetProp.Type) && !IsMapperRegistered(ci.SourceProp.Type, ci.TargetProp.Type)) { var converter = TypeDescriptor.GetConverter(ci.SourceProp.Type); if (converter != null && converter.CanConvertTo(ci.TargetProp.Type)) { ci.TargetProp.Value = converter.ConvertTo(ci.SourceProp.Value, ci.TargetProp.Type); hasConverted = true; } else { converter = TypeDescriptor.GetConverter(ci.TargetProp.Type); if (converter != null && converter.CanConvertFrom(ci.SourceProp.Type)) { ci.TargetProp.Value = converter.ConvertFrom(ci.SourceProp.Value); hasConverted = true; } } } if (!hasConverted) { // If we can't simply map it by setting the value, then we'll send the operation back through the mapper // Also, if it's not a reference type, we can't map it in place and have to set the value of the property to a new value if (TypeFinder.IsImplicitValueType(ci.TargetProp.Type) || ci.TargetProp.Value == null) { //map to new var val = MapToNew(ci.SourceProp.Value, ci.SourceProp.Type, ci.TargetProp.Type, scope); ci.TargetProp.Value = val; } else { //map to existing MapToExisting(ci.SourceProp.Value, ci.TargetProp.Value, ci.SourceProp.Type, ci.TargetProp.Type); } } } SetProperty(reflectedProp, target, ci.TargetProp.Value, scope); //tag this as already mapped AddMemberMapped(ci); } //now that we've mapped by name, lets map by rule foreach (var signature in _mapper.MappingContext.MemberExpressions) { //get the target property definition with the expression mapped var targetProp = targetPropDefs.Single(x => x.expression.Equals(signature)); var reflectedProp = ci.Target.Type.GetProperty(targetProp.property.Name); if (reflectedProp == null) { throw new MissingMemberException("Could not access property " + targetProp.property.Name + " on object Type " + targetProp.property.ComponentType.Name); } //fill in the TypeMapDefinition object ci.TargetProp.Name = targetProp.property.Name; ci.TargetProp.Type = targetProp.property.PropertyType; //now, try to map from a rule, if successful set the value... the MapFromRule will automatically add the Info to the already mapped list. object val; if (!IsAlreadyMapped(ci) && _mapper.MappingContext.MapFromRule(ci, targetProp.expression, out val)) { if (!reflectedProp.CanWrite) { throw new MemberAccessException("A member expression has been declared for a writeable mapping operation for property " + targetProp.property.Name + " on object Type " + targetProp.property.ComponentType.Name + " but this property is readonly and cannot be written to"); } SetProperty(reflectedProp, target, val, scope); //tag this as already mapped AddMemberMapped(ci); } } }