Skip to content

Instantly share code, notes, and snippets.

@rog1039
Created November 27, 2018 19:54
Show Gist options
  • Save rog1039/db1ede2537c9e055ff605ca43d3d9995 to your computer and use it in GitHub Desktop.
Save rog1039/db1ede2537c9e055ff605ca43d3d9995 to your computer and use it in GitHub Desktop.
Repro Test Case for Automapper
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