dotnet / orleans

Cloud Native application framework for .NET
https://docs.microsoft.com/dotnet/orleans
MIT License
10.11k stars 2.04k forks source link

PersistentState cannot handle unserializable types #8625

Closed AdrianoAE closed 1 year ago

AdrianoAE commented 1 year ago

When using IPersistentState the type instantiation is currently using Activator.CreateInstance to activate the state type, as such unserializable types like classes without public constructors are not possible to be instantiated and an exception is thrown

Exc level 0: System.MissingMethodException: Cannot dynamically create an instance of type 'UnitTests.Grains.ExternalTypeWithoutPublicConstructor'. Reason: No parameterless constructor defined.
   at System.RuntimeType.ActivatorCache..ctor(RuntimeType rt)
   at System.RuntimeType.CreateInstanceOfT()
   at System.Activator.CreateInstance[T]()
   at Orleans.Core.StateStorageBridge`1.get_GrainState() in ...\orleans\src\Orleans.Runtime\Storage\StateStorageBridge.cs:line 45
   at Orleans.Core.StateStorageBridge`1.ReadStateAsync() in ...\orleans\src\Orleans.Runtime\Storage\StateStorageBridge.cs:line 77
---- System.MissingMethodException : Cannot dynamically create an instance of type 'UnitTests.Grains.ExternalTypeWithoutPublicConstructor'. Reason: No parameterless constructor defined.
asasine commented 1 year ago

This same exception occurs when using records without parameterless constructors. Refactoring the UrlDetails object from the Orleans quickstart to a record with a primary constructor fails

[GenerateSerializer]
public record class UrlDetails([property: Id(0)] string FullUrl, [property: Id(1)] string ShortenedRouteSegment);

Causes

Orleans.Runtime.OrleansException: Error from storage provider MemoryGrainStorage.url during ReadStateAsync for grain urlshortener/39B5C80

      Exc level 0: System.MissingMethodException: Cannot dynamically create an instance of type 'UrlDetails'. Reason: No parameterless constructor defined.
         at System.RuntimeType.ActivatorCache..ctor(RuntimeType rt)
         at System.RuntimeType.CreateInstanceOfT()
         at System.Activator.CreateInstance[T]()
         at Orleans.Core.StateStorageBridge`1.get_GrainState() in /_/src/Orleans.Runtime/Storage/StateStorageBridge.cs:line 42
         at Orleans.Core.StateStorageBridge`1.ReadStateAsync() in /_/src/Orleans.Runtime/Storage/StateStorageBridge.cs:line 72
       ---> System.MissingMethodException: Cannot dynamically create an instance of type 'UrlDetails'. Reason: No parameterless constructor defined.
         at System.RuntimeType.ActivatorCache..ctor(RuntimeType rt)
         at System.RuntimeType.CreateInstanceOfT()
         at System.Activator.CreateInstance[T]()
         at Orleans.Core.StateStorageBridge`1.get_GrainState() in /_/src/Orleans.Runtime/Storage/StateStorageBridge.cs:line 42
         at Orleans.Core.StateStorageBridge`1.ReadStateAsync() in /_/src/Orleans.Runtime/Storage/StateStorageBridge.cs:line 72
         --- End of inner exception stack trace ---
         at Orleans.Core.StateStorageBridge`1.OnError(Exception exception, ErrorCode id, String operation) in /_/src/Orleans.Runtime/Storage/StateStorageBridge.cs:line 168
         at Orleans.Core.StateStorageBridge`1.ReadStateAsync() in /_/src/Orleans.Runtime/Storage/StateStorageBridge.cs:line 78
         at Orleans.LifecycleSubject.OnStart(CancellationToken cancellationToken) in /_/src/Orleans.Core/Lifecycle/LifecycleSubject.cs:line 118
         at Orleans.Internal.OrleansTaskExtentions.WithCancellation(Task taskToComplete, String message, CancellationToken cancellationToken) in /_/src/Orleans.Core/Async/TaskExtensions.cs:line 145
         at Orleans.Runtime.ActivationData.<ActivateAsync>g__CallActivateAsync|139_0(Dictionary`2 requestContextData, CancellationToken cancellationToken) in /_/src/Orleans.Runtime/Catalog/ActivationData.cs:line 1400
         at Orleans.Serialization.Invocation.ResponseCompletionSource.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token) in /_/src/Orleans.Serialization/Invocation/ResponseCompletionSource.cs:line 98
         at System.Threading.Tasks.ValueTask.ValueTaskSourceAsTask.<>c.<.cctor>b__4_0(Object state)
      --- End of stack trace from previous location ---
         at Program.<>c.<<Main>b__0_2>d.MoveNext() in C:\path\Program.cs:line 41
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Http.RequestDelegateFactory.ExecuteTaskResult[T](Task`1 task, HttpContext httpContext)
         at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

This seems to contradict the documentation that serializing record types is supported.

AdrianoAE commented 1 year ago

PR updated with a unit test for that