Closed vsederburg closed 3 years ago
Hi,
Thanks for letting me know about this - the error looks to be happening cloning the OriginalPropertyValues
dictionary. I'll take a look and report back.
Cheers,
Steve
Hello!
Sorry for the delay - I've been working on a code-generation project to enable build-time generation of Mapper source code, which I'm now going to test with this bug!
Unless I've missed something, I don't seem to have the sources for DtoClassMetadata
and DtoPropertyMetadata
- could you point me in the right direction?
Cheers,
Steve
Ok - I've mocked up the DtoMetadata classes and made a bit of progress - the generated mapping plan for CustomFieldOptionListDto
looks like this:
cfoldToCfoldData =>
{
Issue216.CustomFieldOptionListDto sourceCustomFieldOptionListDto;
try
{
sourceCustomFieldOptionListDto = cfoldToCfoldData.Source;
cfoldToCfoldData.Target.OptionId = sourceCustomFieldOptionListDto.OptionId;
cfoldToCfoldData.Target.CustomColumnId = sourceCustomFieldOptionListDto.CustomColumnId;
cfoldToCfoldData.Target.OptionValue = sourceCustomFieldOptionListDto.OptionValue;
cfoldToCfoldData.Target.SortOrder = sourceCustomFieldOptionListDto.SortOrder;
cfoldToCfoldData.Target.IsDefault = sourceCustomFieldOptionListDto.IsDefault;
cfoldToCfoldData.Target.IsDeleted = sourceCustomFieldOptionListDto.IsDeleted;
cfoldToCfoldData.Target.PlaceholderGuid = sourceCustomFieldOptionListDto.PlaceholderGuid;
cfoldToCfoldData.Target.DataValueProperty = sourceCustomFieldOptionListDto.DataValueProperty;
cfoldToCfoldData.Target.DisplayValueProperty = sourceCustomFieldOptionListDto.DisplayValueProperty;
cfoldToCfoldData.Target.IsDefaultProperty = sourceCustomFieldOptionListDto.IsDefaultProperty;
cfoldToCfoldData.Target.IsDirty = sourceCustomFieldOptionListDto.IsDirty;
if (sourceCustomFieldOptionListDto.OriginalPropertyValues != null)
{
cfoldToCfoldData.Target.OriginalPropertyValues =
{
IObjectMappingData<Dictionary<string, object>, Dictionary<string, object>> sodToSodData;
try
{
sodToSodData = MappingDataFactory.ForChild(
sourceCustomFieldOptionListDto.OriginalPropertyValues,
cfoldToCfoldData.Target.OriginalPropertyValues,
cfoldToCfoldData.ElementIndex,
cfoldToCfoldData.ElementKey,
"OriginalPropertyValues",
0,
cfoldToCfoldData);
var stringObjectDictionary = sodToSodData.Target ?? new Dictionary<string, object>(sodToSodData.Source.Comparer);
var i = 0;
var enumerator = sodToSodData.Source.GetEnumerator();
try
{
while (true)
{
if (!enumerator.MoveNext())
{
break;
}
var originalPropertyValuesKey = enumerator.Current.Key;
if (enumerator.Current.Value == null)
{
stringObjectDictionary[originalPropertyValuesKey] = default(object);
++i;
continue;
}
stringObjectDictionary[originalPropertyValuesKey] = enumerator.Current.Value.GetType().IsSimple()
? enumerator.Current.Value
: sodToSodData.Map(
enumerator.Current.Value,
default(object),
i,
originalPropertyValuesKey);
++i;
}
}
finally
{
enumerator.Dispose();
}
return stringObjectDictionary;
}
catch (Exception ex)
{
throw MappingException.For(
"Overwrite",
"Issue216.CustomFieldOptionListDto.OriginalPropertyValues",
"Issue216.CustomFieldOptionListDto.OriginalPropertyValues",
ex);
}
}
}
else
{
cfoldToCfoldData.Target.OriginalPropertyValues = default(Dictionary<string, object>);
}
cfoldToCfoldData.Target.IsLinkedToDatabase = sourceCustomFieldOptionListDto.IsLinkedToDatabase;
// No data sources for SetNotLinkedToDatabase
return cfoldToCfoldData.Target;
}
catch (Exception ex)
{
throw MappingException.For(
"Overwrite",
"Issue216.CustomFieldOptionListDto",
"Issue216.CustomFieldOptionListDto",
ex);
}
}
I can't see anything obvious which would throw an exception - the OriginalPropertyValues
dictionary cloning is done item-by-item as the values are of type object
, but they should all fall into the GetType().IsSimple()
branch as the only properties included in CustomFieldOptionListDto
are int, string and bool.
Do you get the error if you try ignore OriginalPropertyValues
? Looks like it's self-populating in any case and probably doesn't need to be mapped...
Oh man, I think I get to be a bit embarassed. Yes, marking OriginalPropertyValues to not map does seem to fix it. However, after it wasn't erroring, I realized the save wasn't quite working as expected, and that's when I realized that I was actually calling the Map().Over() to map a record over itself.
The code I provided was:
protected void CloneChangesToMasterRecordList(T recordToClone) { var masterDto = MasterRecordList.FirstOrDefault(p => p.PlaceholderGuid == recordToClone.PlaceholderGuid); if (masterDto != null) { // Update the master list with this one recordToClone.Map().Over(masterDto); } }
but it SHOULD have been:
protected void CloneChangesToMasterRecordList(T recordToClone) { var dto = LocalRecordList.FirstOrDefault(p => p.PlaceholderGuid == recordToClone.PlaceholderGuid); if (dto != null) { // Update the master list with this one recordToClone.Map().Over(masterDto); } }
Basically, find the record in my local list, and then copy it over the corresponding master list record.
Once I fixed that, I removed the "do not map" attribute from OriginalPropertyValues, and it still works fine. So if anything, maybe the mapper could throw a warning if you're mapping an object over itself by mistake? Like if the source and target hash matches? Not sure. You're the expert there... but I think I probably wasted your time with my own error. Very sorry about that.
Thanks for a great program, I truly appreciate it.
Haha! Oh well - all's well that ends well :) Glad it works now - maybe I'll pop a reference equals check in there somewhere.
Glad you find the mapper useful!
All the best,
Steve
I use the Map().Over() all the time. The only thing I can see different here is that the dto being mapped implements an interface that has a Guid defined. Guid PlaceholderGuid { get; }
Code is:
protected void CloneChangesToMasterRecordList(T recordToClone) { var masterDto = MasterRecordList.FirstOrDefault(p => p.PlaceholderGuid == recordToClone.PlaceholderGuid); if (masterDto != null) { // Update the master list with this one recordToClone.Map().Over(masterDto); } }
An exception occurred mapping CustomFieldOptionListDto.OriginalPropertyValues -> CustomFieldOptionListDto.OriginalPropertyValues with rule set Overwrite."} | System.Exception {AgileObjects.AgileMapper.MappingException}
at AgileObjects.AgileMapper.ObjectPopulation.ObjectMapper
2.Map(ObjectMappingData
2 mappingData)\r\n at AgileObjects.AgileMapper.MappingExecutor1.PerformMapping[TTarget](TTarget target)\r\n at AgileObjects.AgileMapper.MappingExecutor
1.PerformMapping[TTarget](MappingRuleSet ruleSet, TTarget target)\r\n at AgileObjects.AgileMapper.MappingExecutor1.Over[TTarget](TTarget existing)\r\n at mt.Shared.Base.MtListMaintParentRemoteViewModel
1.CloneChangesToMasterRecordList(T recordToClone)