Orleans serializer is unable to handle correctly private fields of classes defined in some specific assemblies
Opened this issue · 1 comments
Library name and version
Microsoft.Orleans.Serialization 9.0.1, 7.2.6 (and maybe other versions).
Bug description
We noticed that the Orleans serializer is unable to serialize correctly private fields of classes that are defined in an assembly with a single to Orleans (Microsoft.Orleans.Serialization.Abstractions
NuGet package).
Here is an example of a class that cannot be handled by the serializer:
[GenerateSerializer]
public class MotorBike
{
[Id(0)]
private readonly string brand;
[Id(1)]
private readonly string model;
public MotorBike(string brand, string model)
{
this.brand = brand;
this.model = model;
}
public string Brand => this.brand;
public string Model => this.model;
}
Reproduction Steps
This repository is containing the following unit test that can be used to reproduce the bug:
[TestCaseSource(nameof(GetObjectsToCheck))]
public void ShouldSerializeAndDeserializeObject<T>(T @object)
{
// Arrange
var serializer = cluster!.ServiceProvider.GetRequiredService<Serializer>();
// Act
var binaryData = serializer.SerializeToArray(@object);
var deserializedObject = serializer.Deserialize<T>(binaryData);
// Assert
deserializedObject.Should().BeEquivalentTo(@object);
}
private static IEnumerable<TestCaseData> GetObjectsToCheck()
{
yield return new TestCaseData(new Plane(brand: "Airbus", model: "A350")); // class with private fields defined in the test assembly (✅ passed)
yield return new TestCaseData(new Car(brand: "Alpine", model: "A110")); // class with protected fields defined in another assembly (✅ passed)
yield return new TestCaseData(new MotorBike(brand: "Honda", model: "CB1000R")); // class with private fields defined in another assembly (❌ failed)
yield return new TestCaseData(new Train(brand: "Alstom", model: "TGV M")); // class with public properties defined in another assembly (✅ passed)
}
The test case with MotorBike
object failed with the following error message:
ShouldSerializeAndDeserializeObject<MotorBike>(OrleansSerialization.Contracts.MotorBike)
Source: SerializationTests.cs line 25
Duration: 59 ms
Message:
Expected property deserializedObject.Brand to be "Honda", but found <null>.
Expected property deserializedObject.Model to be "CB1000R", but found <null>.
With configuration:
- Use declared types and members
- Compare enums by value
- Compare tuples by their properties
- Compare anonymous types by their properties
- Compare records by their members
- Include non-browsable members
- Match member by name (or throw)
- Be strict about the order of items in byte arrays
- Without automatic conversion.
Stack Trace:
LateBoundTestFramework.Throw(String message)
TestFrameworkProvider.Throw(String message)
CollectingAssertionStrategy.ThrowIfAny(IDictionary`2 context)
AssertionScope.Dispose()
EquivalencyValidator.AssertEquality(Comparands comparands, EquivalencyValidationContext context)
ObjectAssertions`2.BeEquivalentTo[TExpectation](TExpectation expectation, Func`2 config, String because, Object[] becauseArgs)
ObjectAssertions`2.BeEquivalentTo[TExpectation](TExpectation expectation, String because, Object[] becauseArgs)
SerializationTests.ShouldSerializeAndDeserializeObject[T](T object) line 36
Workaround
We are able to circumvent the problem by changing the visibility of fields in the class to protected. This is clearly not an ideal solution for us.
Could you please take a look ?
Regards,
Ahmed