我正在尝试配置
AutoMapper
以忽略同一类上也具有 Has{PropertyName}
属性的属性,设置为 false
。
这用于映射在 .proto 定义中使用
optional
的 GRPC 类。 (我需要存在检测,因此使用包装类对我没有帮助。)
例如
syntax = "proto3";
message MyGRPCFoo {
optional int32 bar = 1;
}
然后在我的 C# 类中,我需要在使用变量之前检查
HasBar
。
我知道如何为每个会员做到这一点,但我想立即为所有会员实现这一目标。
我尝试了
ShouldMapProperty
,但它只暴露了一个PropertyInfo
,所以我无法获取源对象的实例。
我也尝试过
ForAllMembers
,但是当它公开源对象时,我没有看到如何获取当前的source属性名称,只公开了目标名称。我不想依赖源名称和目标名称相同的假设。
使用以下.proto
message MyGRPCFoo {
optional int32 bar = 1;
}
[TestClass]
public class GRPCTests
{
public class Foo
{
public int Bar { get; set; }
}
[TestMethod]
public void PresenceDetectionTest()
{
var config = new MapperConfiguration(cfg =>
{
var map = cfg.CreateMap<MyGRPCFoo, Foo>();
AddPresenceDetectionForExplicitMappedMembers<MyGRPCFoo, Foo>(cfg);
AddPresenceDetectionForImplicitMappedMembers(map);
});
config.AssertConfigurationIsValid();
// Source Value is 1
var grpcFoo = new MyGRPCFoo() { Bar = 1 };
// But cleared, so should not be applied to target value
grpcFoo.ClearBar();
var mapper = config.CreateMapper();
var target = new Foo() { Bar = 2 };
mapper.Map(grpcFoo, target);
Assert.AreEqual(2, target.Bar);
}
private static void AddPresenceDetectionForExplicitMappedMembers<TSource, TDestination>(IMapperConfigurationExpression cfg)
{
// Use reflection to determine which properties require presence detection
var properties = typeof(TSource).GetProperties();
var propertyLookup = properties.ToDictionary(e => e.Name, e => e); // Name as lookup should be safe because CodeGen doesn't create methods with overloads
// Contains a func to invoke to check if property has presence
var propertyCheck = new Dictionary<MemberInfo, Func<TSource, bool>>();
foreach (var prop in properties)
{
if (propertyLookup.TryGetValue($"Has{prop.Name}", out var propToCheck))
{
Func<TSource, bool> check = (obj) => (bool)propToCheck.GetValue(obj);
propertyCheck.Add(prop, check);
}
}
var @internal = cfg.Internal();
// Although the precondition is added after the previous mapping, it still gets evaluated first
@internal.ForAllPropertyMaps(e => e.SourceMember.DeclaringType == typeof(TSource) && e.DestinationMember.DeclaringType == typeof(TDestination) && propertyCheck.ContainsKey(e.SourceMember),
(propertyMap, ex) => ex.PreCondition((src, ctx) => propertyCheck[propertyMap.SourceMember]((TSource)src)));
}
private static void AddPresenceDetectionForImplicitMappedMembers<TSource, TDestination>(IMappingExpression<TSource, TDestination> map)
{
var srcProps = typeof(TSource).GetProperties();
var dstProps = typeof(TDestination).GetProperties().Select(e => e.Name).ToHashSet();
foreach (var prop in srcProps.Where(e => e.Name.StartsWith("Has")))
{
var targetPropName = prop.Name.Remove(0, "Has".Length);
if (dstProps.Contains(targetPropName))
{
map.ForMember(targetPropName, e => e.PreCondition((src, _) => (bool)prop.GetValue(src)));
}
}
}
}