Как предотвратить перезапись значений в объекте "Target" на нули из объекта "Source" при использовании ValueInjector или Automapper? Вложенная проблема отображения?

Моя проблема

"Исходный" объект Свойства того же класса, которые не существуют в представлении, перезаписывают те же свойства в "целевом" объекте пустыми значениями. Как мне это предотвратить? В действительности, как я могу гарантировать, что только заполненные (не нулевые) свойства объединяются в объект "Target". Я также попробовал это с Automapper и потерпел неудачу, но я был бы счастлив с решением Automapper в качестве альтернативы.

Я действительно ценю, что этот вопрос "Null Mapping" появился раньше, но я боюсь, что моя ситуация более сложна, поскольку есть вложенные объекты. Хорошо я попробовал предложенные варианты, и я не мог заставить их работать. И вот я здесь.

Огромная благодарность за любую помощь.

Я понимаю, что это сложная проблема, и действительно, очень ценю любую помощь в этом, особенно если кто-то может создать пример кода для меня. Я потянул за это несколько дней:(

Что я пытался

У меня есть 2 объекта, один является оригиналом ("Target"), один ("Source") заполняется формой, т.е. представлением.

Исходный "целевой" объект (myOrigDoc) выглядит следующим образом:

Форма "Исходный" объект (myDoc) выглядит следующим образом:

Затем я делаю сопоставление:

            myOrigDoc.Introduction.InjectFrom<StrNotNull>(myDoc.Introduction);

используя следующий инжектор:

    public class StrNotNull: ConventionInjection
{
    bool blnNotNull = false;
    bool blnMatch = false;
    protected override bool Match(ConventionInfo c)
    {
        blnNotNull = false;
        blnMatch = false;

        //if ((c.SourceProp.Type == typeof(string)) && (c.SourceProp.Value != null))
        //    blnAssignable = true;

        if (c.SourceProp.Value != null)
            blnNotNull = true;

        if ((c.SourceProp.Name == c.TargetProp.Name) && (blnNotNull)) 
            blnMatch = true;

        return blnMatch;
    }
}

и я заканчиваю с:

Форма не имеет поля "DateOfBirth", поэтому я подозреваю, что привязка модели создает нулевое значение для свойства "DataOfBirth" в новом объекте "MyDoc", когда я вызываю:

        public ActionResult Index(Document myDoc)

Большое спасибо, Эд.

EDIT1: я считаю, что это проблема вложенного отображения из-за подклассов. Не уверен, как я сортирую это в ValueInjector.

РЕДАКТИРОВАТЬ 2: Возможное решение Automapper из документации для вложенных отображений, но я не мог заставить его работать. Я все еще получаю мои нули, переходящие в цель.

Mapper.CreateMap<XSD_Smart2.Document, XSD_Smart2.Document> 
().ForAllMembers(opt => opt.Condition(srs => !srs.IsSourceValueNull));

Mapper.CreateMap<XSD_Smart2.DocumentIntroduction, XSD_Smart2.DocumentIntroduction>  
().ForAllMembers(opt => opt.Condition(srs => !srs.IsSourceValueNull));

Mapper.CreateMap<XSD_Smart2.Client, XSD_Smart2.Client>().ForAllMembers(opt => 
opt.Condition(srs => !srs.IsSourceValueNull));

2 ответа

Решение

Обновление для ValueInjecter 3

public class IgnoreNulls : LoopInjection
{
    protected override void SetValue(object source, object target, PropertyInfo sp, PropertyInfo tp)
    {
        var val = sp.GetValue(source);
        if (val != null)
        {
            tp.SetValue(target, val);
        }
    }
}

Предыдущая версия

создайте пользовательскую инъекцию, которая будет иметь такое поведение:

    public class IgnoreNulls : ConventionInjection
    {
        protected override bool Match(ConventionInfo c)
        {
            return c.SourceProp.Name == c.TargetProp.Name
                  && c.SourceProp.Value != null;
        }
    }

и использовать это:

    target.InjectFrom<IgnoreNulls>(source);

Это просто AutoMapper тест работает для меня:

Классы

public class Client
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

AutoMapperConfiguration

public class MyProfile : Profile
{
    protected override void Configure()
    {
        CreateMap<Client, Client>()
            .ForAllMembers(opt => opt.Condition(src => !src.IsSourceValueNull));
    }
}

Модульные тесты

[TestFixture]
public class MappingTests
{
    [Test]
    public void AutoMapper_Configuration_IsValid()
    {
        Mapper.Initialize(m => m.AddProfile<MyProfile>());
        Mapper.AssertConfigurationIsValid();
    }

    [Test]
    public void AutoMapper_ClientMapping_IsValid()
    {
        Mapper.Initialize(m => m.AddProfile<MyProfile>());
        Mapper.AssertConfigurationIsValid();

        var source = new Client
            {
                FirstName = "SourceFirstName1",
                LastName = null
            };

        var destination = new Client
            {
                FirstName = "DestinationFirstName1",
                LastName = "DestinationLastName1"
            };

        destination = Mapper.Map(source, destination);

        Assert.That(destination, Is.Not.Null);
        Assert.That(destination.FirstName, Is.EqualTo("SourceFirstName1"));
        Assert.That(destination.LastName, Is.EqualTo("DestinationLastName1"));
    }
}

ОБНОВИТЬ

Интересно, что когда вы используете это отображение для сопоставления списка, оно терпит неудачу. IE - этот тест не пройден:

[Test]
public void AutoMapper_ClientListMapping_IsValid()
{
    Mapper.Initialize(m => m.AddProfile<MyProfile>());
    Mapper.AssertConfigurationIsValid();

    var source = new List<Client>
        {
            new Client
                {
                    FirstName = "SourceFirstName1",
                    LastName = null
                },
            new Client
                {
                    FirstName = null,
                    LastName = "SourceLastName2"
                }
        };

    var destination = new List<Client>
        {
            new Client
                {
                    FirstName = "DestinationFirstName1",
                    LastName = "DestinationLastName1"
                },
            new Client
                {
                    FirstName = "DestinationFirstName2",
                    LastName = "DestinationLastName2"
                }
        };

    destination = Mapper.Map(source, destination);

    Assert.That(destination, Is.Not.Null);
    Assert.That(destination.Count, Is.EqualTo(2));
    Assert.That(destination[0].FirstName, Is.EqualTo("SourceFirstName1"));
    Assert.That(destination[0].LastName, Is.EqualTo("DestinationLastName1"));
    //  /\  Line above went BANG!  /\
    Assert.That(destination[1].FirstName, Is.EqualTo("DestinationFirstName2"));
    Assert.That(destination[1].LastName, Is.EqualTo("SourceLastName2"));
}

Это похоже на ошибку в AutoMapper (в 2.2.0 и 2.2.1-ci9000)

Другие вопросы по тегам