Как предотвратить перезапись значений в объекте "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)