@@ -407,7 +407,7 @@ void HandleColumn(
407407 {
408408 // Note that for stored procedures we always need to send all parameters, regardless of whether the property
409409 // actually changed.
410- writeValue = columnPropagator ? . TryPropagate ( columnMapping , entry )
410+ writeValue = ! columnPropagator ? . TryPropagate ( columnMapping , entry )
411411 ?? ( entry . EntityState == EntityState . Added
412412 || entry . EntityState == EntityState . Deleted
413413 || ColumnModification . IsModified ( entry , property )
@@ -1217,16 +1217,24 @@ public void RecordValue(IColumnMapping mapping, IUpdateEntry entry)
12171217
12181218 break ;
12191219 case EntityState . Added :
1220+ var currentProviderValue = Update . ColumnModification . GetCurrentProviderValue ( entry , property ) ;
1221+ if ( _originalValueInitialized
1222+ && mapping . Column . ProviderValueComparer . Equals ( _originalValue , currentProviderValue ) )
1223+ {
1224+ // This Added entry has the same value as the original (pre-modification) value.
1225+ // It will receive the new value via TryPropagate, so don't let it reset _currentValue or _write.
1226+ break ;
1227+ }
1228+
12201229 if ( _currentValue == null
12211230 || ! property . GetValueComparer ( ) . Equals (
12221231 Update . ColumnModification . GetCurrentValue ( entry , property ) ,
12231232 property . Sentinel ) )
12241233 {
1225- _currentValue = Update . ColumnModification . GetCurrentProviderValue ( entry , property ) ;
1234+ _currentValue = currentProviderValue ;
12261235 }
12271236
1228- _write = ! _originalValueInitialized
1229- || ! mapping . Column . ProviderValueComparer . Equals ( _originalValue , _currentValue ) ;
1237+ _write = true ;
12301238
12311239 break ;
12321240 case EntityState . Deleted :
@@ -1243,40 +1251,73 @@ public void RecordValue(IColumnMapping mapping, IUpdateEntry entry)
12431251 }
12441252 }
12451253
1254+ /// <summary>
1255+ /// Attempts to propagate the recorded value from other enties in this command to the specified entry.
1256+ /// </summary>
1257+ /// <param name="mapping">The column mapping for which the value may be propagated.</param>
1258+ /// <param name="entry">The entry that may receive the propagated value.</param>
1259+ /// <returns>
1260+ /// <see langword="true" /> if no value was recorded (column should not be written), or if the value was
1261+ /// successfully propagated to <paramref name="entry" />.
1262+ /// <see langword="false" /> if a value was recorded for this column (i.e. the column should be written)
1263+ /// but propagation to <paramref name="entry" /> was skipped because the entry is the source of the write;
1264+ /// </returns>
12461265 public bool TryPropagate ( IColumnMappingBase mapping , IUpdateEntry entry )
12471266 {
1267+ if ( ! _write )
1268+ {
1269+ return true ;
1270+ }
1271+
12481272 var property = mapping . Property ;
1249- if ( _write
1250- && ( entry . EntityState == EntityState . Unchanged
1251- || ( entry . EntityState == EntityState . Modified && ! Update . ColumnModification . IsModified ( entry , property ) )
1252- || ( entry . EntityState == EntityState . Added
1253- && ( ( ! _originalValueInitialized
1254- && property . GetValueComparer ( ) . Equals (
1255- Update . ColumnModification . GetCurrentValue ( entry , property ) ,
1256- property . Sentinel ) )
1257- || ( _originalValueInitialized
1258- && mapping . Column . ProviderValueComparer . Equals (
1259- Update . ColumnModification . GetCurrentProviderValue ( entry , property ) ,
1260- _originalValue ) ) ) ) ) )
1273+ var shouldPropagate = entry . EntityState switch
12611274 {
1262- if ( ( property . GetAfterSaveBehavior ( ) == PropertySaveBehavior . Save
1263- || entry . EntityState == EntityState . Added )
1264- && property . ValueGenerated != ValueGenerated . Never )
1265- {
1266- var value = _currentValue ;
1267- var converter = property . GetTypeMapping ( ) . Converter ;
1268- if ( converter != null )
1269- {
1270- value = converter . ConvertFromProvider ( value ) ;
1271- }
1275+ EntityState . Unchanged => true ,
1276+ EntityState . Modified => ! Update . ColumnModification . IsModified ( entry , property ) ,
1277+ EntityState . Added => ShouldPropagateForAddedEntry ( mapping , entry , property ) ,
1278+ _ => false
1279+ } ;
1280+
1281+ if ( ! shouldPropagate )
1282+ {
1283+ return false ;
1284+ }
12721285
1273- Update . ColumnModification . SetStoreGeneratedValue ( entry , property , value ) ;
1286+ if ( ( property . GetAfterSaveBehavior ( ) == PropertySaveBehavior . Save
1287+ || entry . EntityState == EntityState . Added )
1288+ && property . ValueGenerated != ValueGenerated . Never )
1289+ {
1290+ var value = _currentValue ;
1291+ var converter = property . GetTypeMapping ( ) . Converter ;
1292+ if ( converter != null )
1293+ {
1294+ value = converter . ConvertFromProvider ( value ) ;
12741295 }
12751296
1276- return false ;
1297+ Update . ColumnModification . SetStoreGeneratedValue ( entry , property , value ) ;
12771298 }
12781299
1279- return _write ;
1300+ return true ;
1301+
1302+ bool ShouldPropagateForAddedEntry ( IColumnMappingBase mapping , IUpdateEntry entry , IProperty property )
1303+ {
1304+ if ( ! _originalValueInitialized )
1305+ {
1306+ // All entries are in the Added state, this entry still has the sentinel (default) value
1307+ // and the propagated value is different — accept propagation
1308+ return property . GetValueComparer ( ) . Equals (
1309+ Update . ColumnModification . GetCurrentValue ( entry , property ) ,
1310+ property . Sentinel )
1311+ && ! mapping . Column . ProviderValueComparer . Equals (
1312+ _currentValue ,
1313+ Update . ColumnModification . GetCurrentProviderValue ( entry , property ) ) ;
1314+ }
1315+
1316+ // Entry's current value matches the original — it's stale, accept the new propagated value
1317+ return mapping . Column . ProviderValueComparer . Equals (
1318+ Update . ColumnModification . GetCurrentProviderValue ( entry , property ) ,
1319+ _originalValue ) ;
1320+ }
12801321 }
12811322 }
12821323}
0 commit comments