AutoMapper和*是指定的属性

时间:2010-03-09 12:14:22

标签: c# xsd automapper xsd.exe

我有一堆XSD.exe生成的数据协定类,所有可选元素都有一对C#属性,如

int Amount {get; set;}
bool isAmountSpecified {get; set;}

在映射竞技场的另一边,我有一个可以为空的int,如

int? Amount {get; set;}

理想情况下,我希望AutoMapper能够识别这些模式并知道如何在两个方向上映射 ,而无需为每个属性指定映射。这可能吗?

3 个答案:

答案 0 :(得分:2)

好的,昨天我和AutoMapper的作者Jimmy Bogard进行了简短的讨论,基本上我正在寻找的东西目前是不可能的。这些约定的支持将在未来的某个时间实施(如果我理解正确的话):)。

答案 1 :(得分:1)

老实说,我不知道AutoMapper会不会这样做(因为我不太使用AutoMapper),但我知道protobuf-net支持这两种模式,因此你可以使用Serializer.ChangeType<,>(obj)在它们之间进行切换。

当前版本非常依赖于成员上的属性(例如[XmlElement(Order = n)]) - 我不知道这是否会导致问题? 进行中版本支持vanilla类型(没有属性),但尚未完成(但很快)。

示例:

[XmlType]
public class Foo
{
    [XmlElement(Order=1)]
    public int? Value { get; set; }
}
[XmlType]
public class Bar
{
    [XmlElement(Order = 1)]
    public int Value { get; set; }
    [XmlIgnore]
    public bool ValueSpecified { get; set; }
}
static class Program
{
    static void Main()
    {
        Foo foo = new Foo { Value = 123 };
        Bar bar = Serializer.ChangeType<Foo, Bar>(foo);
        Console.WriteLine("{0}, {1}", bar.Value, bar.ValueSpecified);

        foo = new Foo { Value = null };
        bar = Serializer.ChangeType<Foo, Bar>(foo);
        Console.WriteLine("{0}, {1}", bar.Value, bar.ValueSpecified);

        bar = new Bar { Value = 123, ValueSpecified = true };
        foo = Serializer.ChangeType<Bar, Foo>(bar);
        Console.WriteLine(foo.Value);

        bar = new Bar { Value = 123, ValueSpecified = false };
        foo = Serializer.ChangeType<Bar, Foo>(bar);
        Console.WriteLine(foo.Value);
    }
}

答案 2 :(得分:-1)

这里是如何将具有*指定属性(源)的模型映射到具有可空属性(目标)的模型的示例。 我为所有成员配置了Condition方法,该方法将检查source属性是否具有相应的* Specified属性,如果存在,则将检查其值。如果* Specified属性返回false,则不满足条件,并且将跳过映射。

您可以在其他方向上执行相同的操作,但是您不必读取* Specified属性值,而必须对其进行设置。

public void Configure(IMapperConfigurationExpression cfg)
{
    cfg.CreateMap<Source, Destination>()
        .ForAllOtherMembers(opt => opt.PreCondition((srcObj, context) => IsSpecified(srcObj, context, opt)));
}

public static bool IsSpecified<TSource, TDestination, TMember>(TSource source, ResolutionContext context, IMemberConfigurationExpression<TSource, TDestination, TMember> opt)
{
    var dstMemberPropertyInfo = opt.DestinationMember as PropertyInfo;

    // if destination member not nullable, then assume that source member won't have *Specified property
    if (!IsNullableType(dstMemberPropertyInfo.PropertyType))
        return true;

    var config = context.Mapper.ConfigurationProvider;
    var map = config.FindTypeMapFor<TSource, TDestination>();
    var propertyMap = map.PropertyMaps.FirstOrDefault(x => x.DestinationMember.Name == opt.DestinationMember.Name);
    var sourceMembers = new Queue<MemberInfo>(propertyMap.SourceMembers);
    var srcParentType = typeof(TSource);
    var srcParentObj = source as object;

    // get the source parent instance
    while (sourceMembers.Count > 1) // the last item in queue is the SourceMember itself
    {
        var srcParentPropertyInfo = sourceMembers.Dequeue() as PropertyInfo;
        srcParentType = srcParentPropertyInfo.PropertyType;
        srcParentObj = srcParentPropertyInfo.GetValue(srcParentObj);

        // the source parent is not defined, so we can skip this mapping
        if (srcParentObj == null)
            return false;
    }

    var srcMemberSpecifiedPropName = propertyMap.SourceMember.Name + "Specified";
    var srcMemberSpecifiedProp = srcParentType.GetProperty(srcMemberSpecifiedPropName);

    // if there is no *Specified property, then assume value is specified
    return srcMemberSpecifiedProp == null || (bool)srcMemberSpecifiedProp.GetValue(srcParentObj);
}

private bool IsNullableType(Type type) => IsGenericType(type, typeof(Nullable<>));

private bool IsGenericType(Type type, Type genericType) => IsGenericType(type) && type.GetGenericTypeDefinition() == genericType;

private bool IsGenericType(Type type) => type.GetTypeInfo().IsGenericType;