Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion src/Microsoft.AspNetCore.OData/Deltas/DeltaOfT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;
using System.Diagnostics.Contracts;
Expand All @@ -19,6 +20,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.OData.Abstracts;
using Microsoft.AspNetCore.OData.Common;
using Microsoft.AspNetCore.OData.Edm;
using Microsoft.AspNetCore.OData.Extensions;

namespace Microsoft.AspNetCore.OData.Deltas;
Expand Down Expand Up @@ -817,14 +819,49 @@ private bool TrySetPropertyValueInternal(string name, object value)
Type propertyType = cacheHit.Property.PropertyType;
if (value != null && !TypeHelper.IsCollection(propertyType) && !propertyType.IsAssignableFrom(value.GetType()))
{
return false;
if (!TryConvertPropertyValue(propertyType, value, out object convertedValue))
{
return false;
}

value = convertedValue;
}

cacheHit.SetValue(_instance, value);
_changedProperties.Add(name);
return true;
}

private static bool TryConvertPropertyValue(Type propertyType, object value, out object convertedValue)
{
Debug.Assert(propertyType != null, "Argument propertyType is null");
Debug.Assert(value != null, "Argument value is null");

convertedValue = value;

Type targetType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
if (!targetType.IsValueType)
{
return false;
}

try
{
convertedValue = EdmPrimitiveHelper.ConvertPrimitiveValue(value, propertyType);
}
catch (ValidationException)
{
return false;
}

if (convertedValue == null)
{
return propertyType.IsNullable();
}

return propertyType.IsAssignableFrom(convertedValue.GetType());
}

private bool TrySetNestedResourceInternal(string name, object deltaNestedResource)
{
Debug.Assert(name != null, "Argument name is null");
Expand Down
34 changes: 34 additions & 0 deletions test/Microsoft.AspNetCore.OData.Tests/Deltas/DeltaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,40 @@ public void TrySetPropertyValue_ThrowsArgumentNull_name()
ExceptionAssert.ThrowsArgumentNull(() => delta.TrySetPropertyValue(null, "Invalid"), "name");
}

[Fact]
public void TrySetPropertyValue_ReturnsTrue_AndTracksChange_WhenInt64ValueSetOnInt32Property()
{
// Arrange - simulates a boxed Int64 value arriving for an Int32 property (e.g. from JSON deserialization)
Delta<DeltaModel> delta = new Delta<DeltaModel>();
long int64Value = 42L;

// Act
bool result = delta.TrySetPropertyValue("IntProperty", int64Value);

// Assert
Assert.True(result, "TrySetPropertyValue should return true when an Int64 value can be coerced to the Int32 property type.");
Assert.Contains("IntProperty", delta.GetChangedPropertyNames());
delta.TryGetPropertyValue("IntProperty", out object retrievedValue);
Assert.Equal(42, retrievedValue);
}

[Fact]
public void TrySetPropertyValue_ReturnsTrue_AndTracksChange_WhenInt64ValueSetOnNullableInt32Property()
{
// Arrange - simulates a boxed Int64 value arriving for a nullable Int32 property
Delta<DeltaModel> delta = new Delta<DeltaModel>();
long int64Value = 42L;

// Act
bool result = delta.TrySetPropertyValue("NullableIntProperty", int64Value);

// Assert
Assert.True(result, "TrySetPropertyValue should return true when an Int64 value can be coerced to the nullable Int32 property type.");
Assert.Contains("NullableIntProperty", delta.GetChangedPropertyNames());
delta.TryGetPropertyValue("NullableIntProperty", out object retrievedValue);
Assert.Equal((int?)42, retrievedValue);
}

[Fact]
public void TrySetPropertyValue_ThrowsInvalidOperation_IfDynamicContainerWithoutSetter()
{
Expand Down