AutoMapper 忽略可选 GRPC 字段的模式

问题描述 投票:0回答:1

我正在尝试配置

AutoMapper
以忽略同一类上也具有
Has{PropertyName}
属性的属性,设置为
false

这用于映射在 .proto 定义中使用

optional
的 GRPC 类。 (我需要存在检测,因此使用包装类对我没有帮助。)

例如

syntax = "proto3";

message MyGRPCFoo {
    optional int32 bar = 1;
}

然后在我的 C# 类中,我需要在使用变量之前检查

HasBar

我知道如何为每个会员做到这一点,但我想立即为所有会员实现这一目标。

我尝试了

ShouldMapProperty
,但它只暴露了一个
PropertyInfo
,所以我无法获取源对象的实例。

我也尝试过

ForAllMembers
,但是当它公开源对象时,我没有看到如何获取当前的source属性名称,只公开了目标名称。我不想依赖源名称和目标名称相同的假设。

.net-core grpc automapper
1个回答
0
投票

使用以下.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)));
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.