使用C#中另一个POCO的非默认值填充POCO

时间:2015-09-16 18:27:03

标签: c# automapper

我有一个以XML格式发送对象的外部API。在每次更新时,API仅发送自上次更新以来已更改的字段。是否有一个库可以使用非默认值或更改的值更新现有POCO实例?

我在另一个项目中使用了AutoMapper,但它要么不支持这样的方法,要么我是盲目的,无法理解如何将它用于此目的。我知道我可以用AutoMapper创建一个非常复杂的手动映射,但可能有一种使用动态映射的自动方式?

1 个答案:

答案 0 :(得分:1)

建议的similar answer导致了正确的方向。有一些问题具有object类型的相等性,但链接的答案是关键。

以下是完全可重复的单元测试:

using System;
using NUnit.Framework;
using AutoMapper;

namespace MyTests {
    [TestFixture]
    public class AutomappingTests {

        public class IncrementalPOCO
        {
            public int ValueType { get; set; }
            public string RefernceType { get; set; }
        }

        private Func<IncrementalPOCO> getOriginal = () => new IncrementalPOCO()
        {
            ValueType = 123,
            RefernceType = "original text"
        };

        private IncrementalPOCO updateValue = new IncrementalPOCO()
        {
            ValueType = 456
        };

        private IncrementalPOCO updateText = new IncrementalPOCO()
        {
            RefernceType = "updated text"
        };

        private IncrementalPOCO updateWithDefault = new IncrementalPOCO()
        {
            ValueType = 0,
            RefernceType = null
        };


        public static bool Always(object value)
        {
            return true;
        }

        public static bool IsDefault(Type type, object value) {
            if (type.IsValueType) {
                return Activator.CreateInstance(type).Equals(value);
            }
            return null == value;
        }

        public static bool AreDifferent(Type type, object source, object destination) {
            if (type.IsValueType) {
                return !source.Equals(destination);
            }
            return !ReferenceEquals(source, destination);
        }

        [Test]
        public void CouldUpdateValues() {
            // Updates all properties
            var original = getOriginal();
            var map = Mapper.CreateMap<IncrementalPOCO, IncrementalPOCO>();

            map.ForAllMembers(opt => opt.Condition(srs => Always(srs.SourceValue))); // just for overload resolution, `true` doesn't work

            Mapper.Map<IncrementalPOCO, IncrementalPOCO>(updateValue, original);
            Assert.AreEqual(null, original.RefernceType);
            Assert.AreEqual(updateValue.ValueType, original.ValueType);

        }

        [Test]
        public void CouldUpdateNonDefaultValues()
        {
            // should work for value types and reference types (nulls)
            var original = getOriginal();
            var map = Mapper.CreateMap<IncrementalPOCO, IncrementalPOCO>();
            // NB != or simple equility won't work
            map.ForAllMembers(opt => opt.Condition(srs => !(IsDefault(srs.SourceType, srs.SourceValue))) );

            Mapper.Map<IncrementalPOCO, IncrementalPOCO>(updateValue, original);
            Assert.AreEqual("original text", original.RefernceType);
            Assert.AreEqual(updateValue.ValueType, original.ValueType);

            original = getOriginal();
            Mapper.Map<IncrementalPOCO, IncrementalPOCO>(updateText, original);
            Assert.AreEqual("updated text", original.RefernceType);
            Assert.AreEqual(123, original.ValueType);

            // this mapping should not change the original because all new values are default
            original = getOriginal();
            Mapper.Map<IncrementalPOCO, IncrementalPOCO>(updateWithDefault, original);
            Assert.AreEqual("original text", original.RefernceType);
            Assert.AreEqual(123, original.ValueType);
        }

        [Test]
        public void CouldUpdateChangedValues() {
            // should update value types when new value is a default one, but different from origin
            var original = getOriginal();
            var map = Mapper.CreateMap<IncrementalPOCO, IncrementalPOCO>();
            // NB != or simple equility won't work
            map.ForAllMembers(opt => opt.Condition(srs => (AreDifferent(srs.SourceType, srs.SourceValue, srs.DestinationValue))));

            Mapper.Map<IncrementalPOCO, IncrementalPOCO>(updateValue, original);
            Assert.AreEqual(null, original.RefernceType);
            Assert.AreEqual(updateValue.ValueType, original.ValueType);

            original = getOriginal();
            Mapper.Map<IncrementalPOCO, IncrementalPOCO>(updateText, original);
            Assert.AreEqual("updated text", original.RefernceType);
            Assert.AreEqual(0, original.ValueType);

            // this mapping will change the original because all new values are default but different from original
            original = getOriginal();
            Mapper.Map<IncrementalPOCO, IncrementalPOCO>(updateWithDefault, original);
            Assert.AreEqual(null, original.RefernceType);
            Assert.AreEqual(0, original.ValueType);
        }

    }
}