Created
November 27, 2018 19:54
-
-
Save rog1039/db1ede2537c9e055ff605ca43d3d9995 to your computer and use it in GitHub Desktop.
Repro Test Case for Automapper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using System.Runtime.InteropServices; | |
using Shouldly; | |
using Xunit; | |
namespace AutoMapper.UnitTests | |
{ | |
public class HolderTests | |
{ | |
private IMapper _mapper; | |
[Fact()] | |
[Trait("Category", "Instant")] | |
public void MethodName_Floating() | |
{ | |
//Create Mapper. | |
var config = new MapperConfiguration(cfg => | |
{ | |
cfg.CreateMap<ObjectOne, ObjectTwo>(); | |
cfg.CreateMap<ObjectTwo, ObjectOne>(); | |
cfg.AddSupport(); | |
}); | |
config.AssertConfigurationIsValid(); | |
_mapper = config.CreateMapper(); | |
//Map object. | |
var object1 = new ObjectOne() | |
{ | |
Name = "asdf", | |
Boolean = true, | |
Number = 123, | |
Image = new byte[2]{0b10, 0b11} | |
}; | |
var object2 = _mapper.Map<ObjectTwo>(object1); | |
var object1b = _mapper.Map<ObjectOne>(object2); | |
ObjectOne.ObjectOneComparer.Equals(object1, object1b).ShouldBeTrue(); | |
var list = new List<ObjectOne>() {object1, object1b}; | |
Console.WriteLine(string.Join(Environment.NewLine, list)); | |
} | |
} | |
public class Holder<T> | |
{ | |
public T Value { get; set; } | |
public void SetValue(object o) => Value = (T) o; | |
} | |
public class ObjectOne | |
{ | |
private sealed class ObjectOneEqualityComparer : IEqualityComparer<ObjectOne> | |
{ | |
public bool Equals(ObjectOne x, ObjectOne y) | |
{ | |
if (ReferenceEquals(x, y)) return true; | |
if (ReferenceEquals(x, null)) return false; | |
if (ReferenceEquals(y, null)) return false; | |
if (x.GetType() != y.GetType()) return false; | |
return string.Equals(x.Name, y.Name) && x.Boolean == y.Boolean && x.Number == y.Number && | |
x.Image.ByteArrayCompare(y.Image); | |
} | |
public int GetHashCode(ObjectOne obj) | |
{ | |
unchecked | |
{ | |
var hashCode = (obj.Name != null ? obj.Name.GetHashCode() : 0); | |
hashCode = (hashCode * 397) ^ obj.Boolean.GetHashCode(); | |
hashCode = (hashCode * 397) ^ obj.Number.GetHashCode(); | |
hashCode = (hashCode * 397) ^ (obj.Image != null ? obj.Image.GetHashCode() : 0); | |
return hashCode; | |
} | |
} | |
} | |
public static IEqualityComparer<ObjectOne> ObjectOneComparer { get; } = new ObjectOneEqualityComparer(); | |
public string Name { get; set; } | |
public bool Boolean { get; set; } | |
public decimal Number { get; set; } | |
public byte[] Image { get; set; } | |
public byte[] Image2 { get; set; } | |
public override string ToString() | |
{ | |
return $"{Name} - {Boolean} - {Number} - {Image}"; | |
} | |
} | |
public class ObjectTwo | |
{ | |
public Holder<string> Name { get; private set; } = new Holder<string>(); | |
public Holder<bool> Boolean { get; private set; } = new Holder<bool>(); | |
public Holder<decimal> Number { get; private set; } = new Holder<decimal>(); | |
public Holder<byte[]> Image { get; private set; } = new Holder<byte[]>(); | |
public Holder<byte[]> Image2 { get; private set; } = new Holder<byte[]>(); | |
} | |
public static class HolderSupport | |
{ | |
public static void AddSupport(this IMapperConfigurationExpression cfg) | |
{ | |
cfg.ForAllPropertyMaps( | |
z => z.SourceMember.IsHolderType() || z.DestinationMember.IsHolderType(), | |
(map,expression) => | |
{ | |
var destinationMemberPropertyInfo = expression.DestinationMember as PropertyInfo; | |
var sourceMemberPropertyInfo = map.SourceMember as PropertyInfo; | |
if (destinationMemberPropertyInfo == null || sourceMemberPropertyInfo == null) return; | |
if (destinationMemberPropertyInfo.IsHolderType()) | |
{ | |
expression.UseDestinationValue(); | |
expression.MapFrom(new TypeToHolderResolver(), sourceMemberPropertyInfo.Name); | |
} | |
else if (sourceMemberPropertyInfo.IsHolderType()) | |
{ | |
expression.MapFrom(new HolderToTypeResolver(), sourceMemberPropertyInfo.Name); | |
} | |
}); | |
} | |
public static bool IsHolderType(this MemberInfo propertyInfo) | |
{ | |
return propertyInfo.GetMemberType().Name == "Holder`1"; | |
} | |
public static Type GetMemberType(this MemberInfo memberInfo) | |
{ | |
switch (memberInfo) | |
{ | |
case MethodInfo mInfo: | |
return mInfo.ReturnType; | |
case PropertyInfo pInfo: | |
return pInfo.PropertyType; | |
case FieldInfo fInfo: | |
return fInfo.FieldType; | |
case null: | |
throw new ArgumentNullException(nameof(memberInfo)); | |
default: | |
throw new ArgumentOutOfRangeException(nameof(memberInfo)); | |
} | |
} | |
} | |
public class TypeToHolderResolver : IMemberValueResolver<object, object, object, object> | |
{ | |
public object Resolve(object source, object destination, object sourceMember, object destMember, ResolutionContext context) | |
{ | |
dynamic destMemberDynamic = destMember; | |
destMemberDynamic.SetValue(sourceMember); | |
return destMember; | |
} | |
} | |
public class HolderToTypeResolver : IMemberValueResolver<object, object, object, object> | |
{ | |
public object Resolve(object source, object destination, object sourceMember, object destMember, ResolutionContext context) | |
{ | |
dynamic sourceMemberDynamic = sourceMember; | |
return sourceMemberDynamic.Value; | |
} | |
} | |
public static class ByteArrayComparison | |
{ | |
/// <summary> | |
/// Compare two byte arrays for equivalence. | |
/// </summary> | |
/// <param name="b1"></param> | |
/// <param name="b2"></param> | |
/// <returns></returns> | |
public static bool ByteArrayCompare(this byte[] b1, byte[] b2) | |
{ | |
if (b1 == null || b2 == null) return false; | |
if (b1.Length != b2.Length) return false; | |
return memcmp(b1, b2, b1.Length) == 0; | |
} | |
/// <summary> | |
/// Fast, native byte array comparison, but still slower than SequenceEqual with ReadOnlySpan. | |
/// </summary> | |
/// <param name="b1"></param> | |
/// <param name="b2"></param> | |
/// <param name="count"></param> | |
/// <returns></returns> | |
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] | |
static extern int memcmp(byte[] b1, byte[] b2, long count); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment