/// <summary> /// Tries to map a property on a DTO to a method on an entity /// </summary> /// <param name="entity">The entity</param> /// <param name="dto">The DTO</param> /// <param name="dtoProperty">The DTO property</param> /// <param name="mapNestedDtos">If true, all nested DTOs are mapped</param> protected virtual void TryMapToMethod(IAggregateEntity entity, ref object dto, PropertyInfo dtoProperty, bool mapNestedDtos = false) { var methods = GetMappableMethods(entity); var candidates = methods.Where ( _ => _.ReturnType.IsAssignableFrom(dtoProperty.PropertyType) || _.ReturnType.ImplementsInterface(typeof(IAggregateEntity)) ); foreach (var method in candidates) { var matches = method.Name.Contains(dtoProperty.Name); if (matches) { var result = default(object); var success = default(bool); var isAggregate = false; try { result = method.Invoke(entity, null); success = true; } catch (Exception ex) { // NOTE: we are ignoring errors when mapping to methods success = false; Debug.WriteLine($"Entity Mapping Error: {ex.Message}"); } if (success) { if (result != null) { isAggregate = result.GetType().ImplementsInterface(typeof(IAggregateEntity)); } if (isAggregate) { if (mapNestedDtos) { var childDto = Activator.CreateInstance(dtoProperty.PropertyType); Map((IAggregateEntity)result, ref childDto, mapNestedDtos); dtoProperty.SetValue(dto, childDto); } } else { dtoProperty.SetValue(dto, result); } } } } }
/// <summary>Determines if two IAggregateEntity instances are similar.</summary> /// <param name="first">The first IAggregateEntity</param> /// <param name="second">The second IAggregateEntity</param> /// <returns> /// <c>true</c> if the given IAggregateEntity instances are similar; otherwise, <c>false</c>. /// </returns> private static Similarity IsSimilarTo(this IAggregateEntity first, IAggregateEntity second) { var simResults = from e1 in first from e2 in second select e1.IsSimilarTo(e2); return(Similarity.FromRatio(simResults.Select(x => x.Boolean).PercentTrue() / 100)); }
/// <summary> /// Tries to map a property on an entity to a property on a DTO /// </summary> /// <param name="entity">The entity</param> /// <param name="dto">The DTO</param> /// <param name="entityProperty">The entity property</param> /// <param name="dtoProperty">The DTO property</param> /// <param name="mapNestedDtos">If true, all nested DTOs are mapped</param> protected virtual void TryMapProperty ( IAggregateEntity entity, ref object dto, PropertyInfo entityProperty, PropertyInfo dtoProperty, bool mapNestedDtos ) { var entityPropertyType = entityProperty.PropertyType; var dtoPropertyType = dtoProperty.PropertyType; if (dtoPropertyType == entityPropertyType) { var entityPropertyValue = entityProperty.GetValue(entity); var configuration = this.LocaleConfiguration; if (dtoPropertyType == typeof(DateTime)) { var date = (DateTime)entityPropertyValue; entityPropertyValue = date.ToLocalTime(configuration); } if (dtoPropertyType == typeof(DateTime?)) { var date = (DateTime?)entityPropertyValue; entityPropertyValue = date.ToLocalTime(configuration); } dtoProperty.SetValue(dto, entityPropertyValue); } else if (dtoPropertyType == typeof(string)) { var entityPropertyValue = entityProperty.GetValue(entity); if (entityPropertyValue != null) { dtoProperty.SetValue(dto, entityPropertyValue.ToString()); } else { dtoProperty.SetValue(dto, null); } } else if (mapNestedDtos) { MapNestedProperty(entity, ref dto, entityProperty, dtoProperty); } }
/// <summary> /// Maps a single entity to a DTO /// </summary> /// <param name="entity">The entity to map</param> /// <param name="dto">The DTO to map to</param> /// <param name="mapNestedDtos">If true, all nested DTOs are mapped</param> /// <returns>The mapped DTO</returns> protected virtual void Map(IAggregateEntity entity, ref object dto, bool mapNestedDtos) { if (entity == null) { dto = null; return; } else { var entityProperties = GetMappableProperties(entity); var dtoProperties = GetMappableProperties(dto); var mappedNames = new List <string>(); foreach (var dtoProperty in dtoProperties) { var isKeyProperty = dtoProperty.Name.Equals("Key", StringComparison.InvariantCultureIgnoreCase); if (isKeyProperty) { dtoProperty.SetValue(dto, entity.LookupKey); } else { var alreadyMapped = mappedNames.Contains(dtoProperty.Name); if (alreadyMapped) { continue; } var entityProperty = entityProperties.FirstOrDefault(_ => _.Name == dtoProperty.Name); if (entityProperty != null) { TryMapProperty(entity, ref dto, entityProperty, dtoProperty, mapNestedDtos); } else { TryMapToMethod(entity, ref dto, dtoProperty, mapNestedDtos); } mappedNames.Add(dtoProperty.Name); } } } }
/// <summary> /// Tries to map a property on a DTO to a property on an entity /// </summary> /// <param name="entity">The entity</param> /// <param name="dto">The DTO</param> /// <param name="entityProperty">The entity property</param> /// <param name="dtoProperty">The DTO property</param> /// <param name="mapNestedDtos">If true, all nested DTOs are mapped</param> protected virtual void TryMapProperty ( IAggregateEntity entity, ref object dto, PropertyInfo entityProperty, PropertyInfo dtoProperty, bool mapNestedDtos ) { var dtoPropertyType = dtoProperty.PropertyType; var entityPropertyValue = entityProperty.GetValue ( entity ); if (dtoPropertyType == entityProperty.PropertyType) { if (dtoPropertyType == typeof(DateTime)) { var date = (DateTime)entityPropertyValue; entityPropertyValue = date.ToLocalTime ( this.LocaleConfiguration ); } if (dtoPropertyType == typeof(DateTime?)) { var date = (DateTime?)entityPropertyValue; entityPropertyValue = date.ToLocalTime ( this.LocaleConfiguration ); } dtoProperty.SetValue ( dto, entityPropertyValue ); } else if (dtoPropertyType == typeof(string)) { if (entityPropertyValue != null) { dtoProperty.SetValue ( dto, entityPropertyValue.ToString() ); } } else if (mapNestedDtos) { var isList = ( dtoPropertyType.IsGenericType && dtoPropertyType.GetGenericTypeDefinition() == typeof(List <>) ); if (isList) { var dtoListType = dtoPropertyType.GetGenericArguments()[0]; var entityListType = entityProperty.PropertyType.GetGenericArguments()[0]; var isValidDto = IsValidDtoType ( dtoListType, entityListType ); if (false == isValidDto) { throw new InvalidOperationException ( "The list type is not a valid DTO." ); } var isValidEntityCollection = entityListType.ImplementsInterface ( typeof(IAggregateEntity) ); if (false == isValidEntityCollection) { var typeName = entityListType.Name; var propertyName = entityProperty.Name; throw new InvalidOperationException ( $"{typeName} on {propertyName} does not implement IAggregateEntity." ); } var dtoList = (IList)Activator.CreateInstance ( dtoPropertyType ); var entityCollection = (IEnumerable)entityProperty.GetValue ( entity ); foreach (IAggregateEntity childEntity in entityCollection) { var childDto = Activator.CreateInstance ( dtoListType ); Map(childEntity, ref childDto, mapNestedDtos); dtoList.Add(childDto); } dtoProperty.SetValue(dto, dtoList); } else { var isValidDto = IsValidDtoType ( dtoPropertyType, entityProperty.PropertyType ); if (isValidDto) { var childEntity = (IAggregateEntity)entityProperty.GetValue ( entity ); var childDto = Activator.CreateInstance ( dtoPropertyType ); Map(childEntity, ref childDto, mapNestedDtos); dtoProperty.SetValue(dto, childDto); } else { throw new InvalidOperationException ( $"The property {dtoProperty.Name} could not be mapped." ); } } } }
/// <summary> /// Maps a nested property on an entity to a property on a DTO /// </summary> /// <param name="entity">The entity</param> /// <param name="dto">The DTO</param> /// <param name="entityProperty">The entity property</param> /// <param name="dtoProperty">The DTO property</param> protected virtual void MapNestedProperty ( IAggregateEntity entity, ref object dto, PropertyInfo entityProperty, PropertyInfo dtoProperty ) { var entityPropertyType = entityProperty.PropertyType; var dtoPropertyType = dtoProperty.PropertyType; var isList = (dtoPropertyType.IsGenericType && dtoPropertyType.GetGenericTypeDefinition() == typeof(List <>)); if (isList) { var dtoListType = dtoPropertyType.GetGenericArguments()[0]; var entityListType = entityPropertyType.GetGenericArguments()[0]; var isValidDto = IsValidDtoType(dtoListType, entityListType); if (false == isValidDto) { throw new InvalidOperationException("The list type is not a valid DTO."); } var isValidEntityCollection = entityListType.ImplementsInterface(typeof(IAggregateEntity)); if (false == isValidEntityCollection) { var typeName = entityListType.Name; var propertyName = entityProperty.Name; throw new InvalidOperationException ( $"{typeName} on {propertyName} does not implement {nameof(IAggregateEntity)}." ); } var dtoList = (IList)Activator.CreateInstance(dtoPropertyType); var entityCollection = (IEnumerable)entityProperty.GetValue(entity); foreach (IAggregateEntity childEntity in entityCollection) { var childDto = Activator.CreateInstance(dtoListType); Map(childEntity, ref childDto, true); dtoList.Add(childDto); } dtoProperty.SetValue(dto, dtoList); } else { var isValidDto = IsValidDtoType(dtoPropertyType, entityPropertyType); if (isValidDto) { var childEntity = (IAggregateEntity)entityProperty.GetValue(entity); var childDto = Activator.CreateInstance(dtoPropertyType); Map(childEntity, ref childDto, true); dtoProperty.SetValue(dto, childDto); } else { throw new InvalidOperationException ( $"Property '{dtoProperty.Name}' could not be mapped. " + $"Type {dtoPropertyType.Name} cannot be converted to " + $"{entityPropertyType.Name}." ); } } }